From 227c9b1dfa22bb7b1a090bc89f1a5089bebb91b9 Mon Sep 17 00:00:00 2001 From: muit Date: Sat, 16 Sep 2023 11:42:29 +0200 Subject: [PATCH 1/7] 5.2 Update + SaveSlot refactor [1/X] --- SaveExtension.uplugin | 2 +- .../Asset/AssetTypeAction_SavePreset.cpp | 4 +- .../Asset/AssetTypeAction_SavePreset.h | 18 +- ...tData.cpp => AssetTypeAction_SaveSlot.cpp} | 10 +- .../Private/Asset/AssetTypeAction_SaveSlot.h | 28 ++ ...o.cpp => AssetTypeAction_SaveSlotData.cpp} | 10 +- .../Asset/AssetTypeAction_SaveSlotData.h | 28 ++ .../Private/Asset/AssetTypeAction_SlotData.h | 28 -- .../Private/Asset/AssetTypeAction_SlotInfo.h | 28 -- .../Private/Asset/SavePresetFactory.cpp | 17 +- .../Editor/Private/Asset/SavePresetFactory.h | 10 +- .../Private/Asset/SaveSlotDataFactory.cpp | 26 ++ .../Private/Asset/SaveSlotDataFactory.h | 22 ++ .../Editor/Private/Asset/SaveSlotFactory.cpp | 26 ++ Source/Editor/Private/Asset/SaveSlotFactory.h | 22 ++ .../Editor/Private/Asset/SlotDataFactory.cpp | 21 - Source/Editor/Private/Asset/SlotDataFactory.h | 22 -- .../Editor/Private/Asset/SlotInfoFactory.cpp | 21 - Source/Editor/Private/Asset/SlotInfoFactory.h | 22 -- .../ClassFilter/ClassFilterHelpers.cpp | 148 ++++--- .../ClassFilter/ClassFilterHelpers.h | 276 +++++++------ .../ClassFilter/ClassFilterNode.cpp | 54 +-- .../ClassFilter/ClassFilterNode.h | 31 +- .../ClassFilter/SClassFilter.cpp | 374 ++++++++---------- .../Customizations/ClassFilter/SClassFilter.h | 46 +-- .../Customizations/SClassFilterGraphPin.cpp | 93 ++--- .../Customizations/SClassFilterGraphPin.h | 19 +- .../SEActorClassFilterCustomization.cpp | 8 +- .../SEActorClassFilterCustomization.h | 16 +- .../SEClassFilterCustomization.cpp | 70 ++-- .../SEClassFilterCustomization.h | 39 +- .../SEClassFilterGraphPanelPinFactory.h | 13 +- .../SEComponentClassFilterCustomization.cpp | 10 +- .../SEComponentClassFilterCustomization.h | 16 +- .../Customizations/SavePresetDetails.cpp | 47 +-- .../Customizations/SavePresetDetails.h | 13 +- Source/Editor/Private/SaveExtensionEditor.cpp | 55 +-- Source/Editor/Private/SaveExtensionEditor.h | 82 ++-- Source/Editor/Public/ISaveExtensionEditor.h | 11 +- Source/Editor/SaveExtensionEditor.Build.cs | 15 +- Source/SaveExtension/Private/FileAdapter.cpp | 106 ++--- .../LatentActions/DeleteSlotsAction.cpp | 7 +- .../Private/LatentActions/LoadGameAction.cpp | 16 +- .../Private/LatentActions/LoadInfosAction.cpp | 18 +- .../Private/LatentActions/SaveGameAction.cpp | 17 +- Source/SaveExtension/Private/LevelFilter.cpp | 10 +- .../Private/LevelStreamingNotifier.cpp | 2 +- .../Private/LifetimeComponent.cpp | 11 +- .../Private/Misc/ClassFilter.cpp | 19 +- .../Private/Misc/SlotHelpers.cpp | 10 +- .../Multithreading/DeleteSlotsTask.cpp | 18 +- .../Private/Multithreading/LoadFileTask.cpp | 2 +- .../Multithreading/LoadSlotInfosTask.cpp | 20 +- .../Private/Multithreading/SaveFileTask.cpp | 2 +- .../Multithreading/ScopedTaskManager.cpp | 2 +- .../SaveExtension/Private/SaveExtension.cpp | 2 +- Source/SaveExtension/Private/SaveExtension.h | 15 +- .../Private/SaveExtensionInterface.cpp | 4 +- .../Private/SaveExtensionLibrary.cpp | 9 +- .../Private/SaveExtensionLibrary.h | 12 +- Source/SaveExtension/Private/SaveManager.cpp | 151 +++---- Source/SaveExtension/Private/SavePreset.cpp | 20 +- .../Private/{SlotInfo.cpp => SaveSlot.cpp} | 65 ++- Source/SaveExtension/Private/SaveSlotData.cpp | 36 ++ .../Private/Serialization/LevelRecords.cpp | 9 +- .../Private/Serialization/MTTask.cpp | 2 +- .../Serialization/MTTask_SerializeActors.cpp | 25 +- .../Private/Serialization/Records.cpp | 7 +- .../Private/Serialization/SEArchive.cpp | 8 +- .../Private/Serialization/SlotDataTask.cpp | 32 +- .../SlotDataTask_LevelLoader.cpp | 8 +- .../Serialization/SlotDataTask_LevelSaver.cpp | 4 +- .../Serialization/SlotDataTask_Loader.cpp | 184 +++++---- .../Serialization/SlotDataTask_Saver.cpp | 103 +++-- Source/SaveExtension/Private/SlotData.cpp | 45 --- Source/SaveExtension/Public/Delegates.h | 12 +- Source/SaveExtension/Public/FileAdapter.h | 59 +-- Source/SaveExtension/Public/ISaveExtension.h | 37 +- .../Public/LatentActions/DeleteSlotsAction.h | 7 +- .../Public/LatentActions/LoadGameAction.h | 9 +- .../Public/LatentActions/LoadInfosAction.h | 14 +- .../Public/LatentActions/SaveGameAction.h | 10 +- Source/SaveExtension/Public/LevelFilter.h | 36 +- .../Public/LevelStreamingNotifier.h | 52 ++- .../SaveExtension/Public/LifetimeComponent.h | 33 +- .../SaveExtension/Public/Misc/ClassFilter.h | 34 +- .../SaveExtension/Public/Misc/SlotHelpers.h | 9 +- Source/SaveExtension/Public/Misc/TypeTraits.h | 16 +- .../Public/Multithreading/Delegates.h | 4 +- .../Public/Multithreading/DeleteSlotsTask.h | 14 +- .../Public/Multithreading/LoadFileTask.h | 25 +- .../Public/Multithreading/LoadSlotInfosTask.h | 20 +- .../Public/Multithreading/SaveFileTask.h | 24 +- .../Public/Multithreading/ScopedTaskManager.h | 29 +- .../Public/SaveExtensionInterface.h | 8 +- Source/SaveExtension/Public/SaveManager.h | 211 +++++----- Source/SaveExtension/Public/SavePreset.h | 135 +++---- Source/SaveExtension/Public/SaveSettings.h | 40 +- Source/SaveExtension/Public/SaveSlot.h | 151 +++++++ .../Public/{SlotData.h => SaveSlotData.h} | 23 +- .../Public/Serialization/LevelRecords.h | 15 +- .../Public/Serialization/MTTask.h | 16 +- .../Serialization/MTTask_SerializeActors.h | 33 +- .../Public/Serialization/Records.h | 23 +- .../Public/Serialization/SlotDataTask.h | 66 ++-- .../Serialization/SlotDataTask_LevelLoader.h | 13 +- .../Serialization/SlotDataTask_LevelSaver.h | 16 +- .../Serialization/SlotDataTask_Loader.h | 42 +- .../Public/Serialization/SlotDataTask_Saver.h | 39 +- Source/SaveExtension/Public/SlotInfo.h | 83 ---- Source/SaveExtension/SaveExtension.Build.cs | 21 +- Source/Test/Private/Files.spec.cpp | 28 +- Source/Test/Private/GameInstance.spec.cpp | 11 +- Source/Test/Private/Helpers/TestActor.h | 55 +-- .../Test/Private/Helpers/TestGameInstance.h | 11 +- Source/Test/Private/Save.spec.cpp | 29 +- Source/Test/Private/SaveExtensionTest.cpp | 4 +- Source/Test/Public/SaveExtensionTest.h | 2 +- Source/Test/SaveExtensionTest.Build.cs | 15 +- 119 files changed, 2244 insertions(+), 2092 deletions(-) rename Source/Editor/Private/Asset/{AssetTypeAction_SlotData.cpp => AssetTypeAction_SaveSlot.cpp} (53%) create mode 100644 Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.h rename Source/Editor/Private/Asset/{AssetTypeAction_SlotInfo.cpp => AssetTypeAction_SaveSlotData.cpp} (51%) create mode 100644 Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.h delete mode 100644 Source/Editor/Private/Asset/AssetTypeAction_SlotData.h delete mode 100644 Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.h create mode 100644 Source/Editor/Private/Asset/SaveSlotDataFactory.cpp create mode 100644 Source/Editor/Private/Asset/SaveSlotDataFactory.h create mode 100644 Source/Editor/Private/Asset/SaveSlotFactory.cpp create mode 100644 Source/Editor/Private/Asset/SaveSlotFactory.h delete mode 100644 Source/Editor/Private/Asset/SlotDataFactory.cpp delete mode 100644 Source/Editor/Private/Asset/SlotDataFactory.h delete mode 100644 Source/Editor/Private/Asset/SlotInfoFactory.cpp delete mode 100644 Source/Editor/Private/Asset/SlotInfoFactory.h rename Source/SaveExtension/Private/{SlotInfo.cpp => SaveSlot.cpp} (62%) create mode 100644 Source/SaveExtension/Private/SaveSlotData.cpp delete mode 100644 Source/SaveExtension/Private/SlotData.cpp create mode 100644 Source/SaveExtension/Public/SaveSlot.h rename Source/SaveExtension/Public/{SlotData.h => SaveSlotData.h} (75%) delete mode 100644 Source/SaveExtension/Public/SlotInfo.h diff --git a/SaveExtension.uplugin b/SaveExtension.uplugin index 3a1ba45..40c8e8b 100644 --- a/SaveExtension.uplugin +++ b/SaveExtension.uplugin @@ -10,7 +10,7 @@ "CreatedByURL": "https://piperift.com", "DocsURL": "https://piperift.com/SaveExtension/", "SupportURL": "info@piperift.com", - "EngineVersion": "4.26", + "EngineVersion": "5.2", "EnabledByDefault": true, "CanContainContent": false, "IsBetaVersion": false, diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp index c6c5233..ef30e22 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "AssetTypeAction_SavePreset.h" @@ -10,7 +10,7 @@ FText FAssetTypeAction_SavePreset::GetName() const { - return LOCTEXT("FAssetTypeAction_SavePresetName", "Save Preset"); + return LOCTEXT("FAssetTypeAction_SavePresetName", "Save Preset"); } FColor FAssetTypeAction_SavePreset::GetTypeColor() const diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h b/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h index 09cf3a7..5a446bb 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h +++ b/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h @@ -1,28 +1,28 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "SaveExtensionEditor.h" -#include "AssetTypeActions_Base.h" - -#include "SavePreset.h" - -#define LOCTEXT_NAMESPACE "SaveExtensionEditor" +#include +#include class FAssetTypeAction_SavePreset : public FAssetTypeActions_Base { public: - - virtual uint32 GetCategories() override { + virtual uint32 GetCategories() override + { return FSaveExtensionEditor::Get().AssetCategory; } virtual FText GetName() const override; virtual FColor GetTypeColor() const override; - virtual UClass* GetSupportedClass() const override { return USavePreset::StaticClass(); } + virtual UClass* GetSupportedClass() const override + { + return USavePreset::StaticClass(); + } }; #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SlotData.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp similarity index 53% rename from Source/Editor/Private/Asset/AssetTypeAction_SlotData.cpp rename to Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp index 77ecde3..679ce81 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SlotData.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp @@ -1,6 +1,6 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. -#include "AssetTypeAction_SlotData.h" +#include "AssetTypeAction_SaveSlot.h" #define LOCTEXT_NAMESPACE "AssetTypeActions" @@ -8,12 +8,12 @@ ////////////////////////////////////////////////////////////////////////// // FAssetTypeAction_SavePreset -FText FAssetTypeAction_SlotData::GetName() const +FText FAssetTypeAction_SaveSlot::GetName() const { - return LOCTEXT("FAssetTypeAction_SlotDataName", "Save Data"); + return LOCTEXT("FAssetTypeAction_SaveSlotName", "Save Info"); } -FColor FAssetTypeAction_SlotData::GetTypeColor() const +FColor FAssetTypeAction_SaveSlot::GetTypeColor() const { return FColor(63, 126, 255); } diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.h b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.h new file mode 100644 index 0000000..eb8cb58 --- /dev/null +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.h @@ -0,0 +1,28 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#pragma once + +#include "SaveExtensionEditor.h" + +#include +#include + + +class FAssetTypeAction_SaveSlot : public FAssetTypeActions_Base +{ +public: + virtual uint32 GetCategories() override + { + return FSaveExtensionEditor::Get().AssetCategory; + } + + virtual FText GetName() const override; + virtual FColor GetTypeColor() const override; + + virtual UClass* GetSupportedClass() const override + { + return USaveSlot::StaticClass(); + } +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp similarity index 51% rename from Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.cpp rename to Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp index 1da57f5..4d226a9 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp @@ -1,6 +1,6 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. -#include "AssetTypeAction_SlotInfo.h" +#include "AssetTypeAction_SaveSlotData.h" #define LOCTEXT_NAMESPACE "AssetTypeActions" @@ -8,12 +8,12 @@ ////////////////////////////////////////////////////////////////////////// // FAssetTypeAction_SavePreset -FText FAssetTypeAction_SlotInfo::GetName() const +FText FAssetTypeAction_SaveSlotData::GetName() const { - return LOCTEXT("FAssetTypeAction_SlotInfoName", "Save Info"); + return LOCTEXT("FAssetTypeAction_SaveSlotDataName", "Save Data"); } -FColor FAssetTypeAction_SlotInfo::GetTypeColor() const +FColor FAssetTypeAction_SaveSlotData::GetTypeColor() const { return FColor(63, 126, 255); } diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.h b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.h new file mode 100644 index 0000000..d8caa25 --- /dev/null +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.h @@ -0,0 +1,28 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#pragma once + +#include "SaveExtensionEditor.h" + +#include +#include + + +class FAssetTypeAction_SaveSlotData : public FAssetTypeActions_Base +{ +public: + virtual uint32 GetCategories() override + { + return FSaveExtensionEditor::Get().AssetCategory; + } + + virtual FText GetName() const override; + virtual FColor GetTypeColor() const override; + + virtual UClass* GetSupportedClass() const override + { + return USaveSlotData::StaticClass(); + } +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SlotData.h b/Source/Editor/Private/Asset/AssetTypeAction_SlotData.h deleted file mode 100644 index a65877b..0000000 --- a/Source/Editor/Private/Asset/AssetTypeAction_SlotData.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once - -#include "SaveExtensionEditor.h" - -#include "AssetTypeActions_Base.h" - -#include "SlotData.h" - -#define LOCTEXT_NAMESPACE "SaveExtensionEditor" - - -class FAssetTypeAction_SlotData : public FAssetTypeActions_Base -{ -public: - - virtual uint32 GetCategories() override { - return FSaveExtensionEditor::Get().AssetCategory; - } - - virtual FText GetName() const override; - virtual FColor GetTypeColor() const override; - - virtual UClass* GetSupportedClass() const override { return USlotData::StaticClass(); } -}; - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.h b/Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.h deleted file mode 100644 index f56b3d9..0000000 --- a/Source/Editor/Private/Asset/AssetTypeAction_SlotInfo.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once - -#include "SaveExtensionEditor.h" - -#include "AssetTypeActions_Base.h" - -#include "SlotInfo.h" - -#define LOCTEXT_NAMESPACE "SaveExtensionEditor" - - -class FAssetTypeAction_SlotInfo : public FAssetTypeActions_Base -{ -public: - - virtual uint32 GetCategories() override { - return FSaveExtensionEditor::Get().AssetCategory; - } - - virtual FText GetName() const override; - virtual FColor GetTypeColor() const override; - - virtual UClass* GetSupportedClass() const override { return USlotInfo::StaticClass(); } -}; - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/SavePresetFactory.cpp b/Source/Editor/Private/Asset/SavePresetFactory.cpp index c33ef3c..e09eb92 100644 --- a/Source/Editor/Private/Asset/SavePresetFactory.cpp +++ b/Source/Editor/Private/Asset/SavePresetFactory.cpp @@ -1,21 +1,26 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. -#include "SavePresetFactory.h" -#include "Kismet2/KismetEditorUtilities.h" +#include "Asset/SavePresetFactory.h" +#include -USavePresetFactory::USavePresetFactory() : Super() { + +USavePresetFactory::USavePresetFactory() : Super() +{ bCreateNew = true; bEditAfterNew = true; SupportedClass = USavePreset::StaticClass(); } -UObject* USavePresetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { +UObject* USavePresetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, + EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ check(Class->IsChildOf(USavePreset::StaticClass())); if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(Class)) { return nullptr; } - return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(),TEXT("AssetTypeActions")); + return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, + UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); } diff --git a/Source/Editor/Private/Asset/SavePresetFactory.h b/Source/Editor/Private/Asset/SavePresetFactory.h index 55f5edf..3870356 100644 --- a/Source/Editor/Private/Asset/SavePresetFactory.h +++ b/Source/Editor/Private/Asset/SavePresetFactory.h @@ -1,23 +1,23 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include "SavePreset.h" - #include "AssetTypeActions_Base.h" #include "Factories/Factory.h" +#include "SavePreset.h" #include "SavePresetFactory.generated.h" + UCLASS() class SAVEEXTENSIONEDITOR_API USavePresetFactory : public UFactory { GENERATED_BODY() public: - USavePresetFactory(); - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, + UObject* Context, FFeedbackContext* Warn) override; }; diff --git a/Source/Editor/Private/Asset/SaveSlotDataFactory.cpp b/Source/Editor/Private/Asset/SaveSlotDataFactory.cpp new file mode 100644 index 0000000..04cea42 --- /dev/null +++ b/Source/Editor/Private/Asset/SaveSlotDataFactory.cpp @@ -0,0 +1,26 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Asset/SaveSlotDataFactory.h" + +#include + + +USaveSaveSlotDataFactory::USaveSaveSlotDataFactory() : Super() +{ + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = USaveSlotData::StaticClass(); +} + +UObject* USaveSaveSlotDataFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, + EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) +{ + check(Class->IsChildOf(USaveSlotData::StaticClass())); + + if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(Class)) + { + return nullptr; + } + return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, + UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); +} diff --git a/Source/Editor/Private/Asset/SaveSlotDataFactory.h b/Source/Editor/Private/Asset/SaveSlotDataFactory.h new file mode 100644 index 0000000..818f15a --- /dev/null +++ b/Source/Editor/Private/Asset/SaveSlotDataFactory.h @@ -0,0 +1,22 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#pragma once + +#include "AssetTypeActions_Base.h" +#include "Factories/Factory.h" +#include "SaveSlotData.h" + +#include "SaveSlotDataFactory.generated.h" + + +UCLASS() +class SAVEEXTENSIONEDITOR_API USaveSaveSlotDataFactory : public UFactory +{ + GENERATED_BODY() + +public: + USaveSaveSlotDataFactory(); + + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, + UObject* Context, FFeedbackContext* Warn) override; +}; diff --git a/Source/Editor/Private/Asset/SaveSlotFactory.cpp b/Source/Editor/Private/Asset/SaveSlotFactory.cpp new file mode 100644 index 0000000..3ad9e40 --- /dev/null +++ b/Source/Editor/Private/Asset/SaveSlotFactory.cpp @@ -0,0 +1,26 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Asset/SaveSlotFactory.h" + +#include + + +USaveSlotFactory::USaveSlotFactory() : Super() +{ + bCreateNew = true; + bEditAfterNew = true; + SupportedClass = USaveSlot::StaticClass(); +} + +UObject* USaveSlotFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, + UObject* Context, FFeedbackContext* Warn) +{ + check(Class->IsChildOf(USaveSlot::StaticClass())); + + if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(Class)) + { + return nullptr; + } + return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, + UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); +} diff --git a/Source/Editor/Private/Asset/SaveSlotFactory.h b/Source/Editor/Private/Asset/SaveSlotFactory.h new file mode 100644 index 0000000..dbf1df2 --- /dev/null +++ b/Source/Editor/Private/Asset/SaveSlotFactory.h @@ -0,0 +1,22 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#pragma once + +#include "AssetTypeActions_Base.h" +#include "Factories/Factory.h" +#include "SaveSlot.h" + +#include "SaveSlotFactory.generated.h" + + +UCLASS() +class SAVEEXTENSIONEDITOR_API USaveSlotFactory : public UFactory +{ + GENERATED_BODY() + +public: + USaveSlotFactory(); + + virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, + UObject* Context, FFeedbackContext* Warn) override; +}; diff --git a/Source/Editor/Private/Asset/SlotDataFactory.cpp b/Source/Editor/Private/Asset/SlotDataFactory.cpp deleted file mode 100644 index c37c8cf..0000000 --- a/Source/Editor/Private/Asset/SlotDataFactory.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#include "SlotDataFactory.h" -#include "Kismet2/KismetEditorUtilities.h" - - -USlotDataFactory::USlotDataFactory() : Super() { - bCreateNew = true; - bEditAfterNew = true; - SupportedClass = USlotData::StaticClass(); -} - -UObject* USlotDataFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { - check(Class->IsChildOf(USlotData::StaticClass())); - - if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(Class)) - { - return nullptr; - } - return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); -} diff --git a/Source/Editor/Private/Asset/SlotDataFactory.h b/Source/Editor/Private/Asset/SlotDataFactory.h deleted file mode 100644 index fcd340f..0000000 --- a/Source/Editor/Private/Asset/SlotDataFactory.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once - -#include "SlotData.h" - -#include "AssetTypeActions_Base.h" -#include "Factories/Factory.h" - -#include "SlotDataFactory.generated.h" - - -UCLASS() -class SAVEEXTENSIONEDITOR_API USlotDataFactory : public UFactory { - GENERATED_BODY() - -public: - - USlotDataFactory(); - - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; -}; diff --git a/Source/Editor/Private/Asset/SlotInfoFactory.cpp b/Source/Editor/Private/Asset/SlotInfoFactory.cpp deleted file mode 100644 index 0bdfa8e..0000000 --- a/Source/Editor/Private/Asset/SlotInfoFactory.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#include "SlotInfoFactory.h" -#include "Kismet2/KismetEditorUtilities.h" - - -USlotInfoFactory::USlotInfoFactory() : Super() { - bCreateNew = true; - bEditAfterNew = true; - SupportedClass = USlotInfo::StaticClass(); -} - -UObject* USlotInfoFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) { - check(Class->IsChildOf(USlotInfo::StaticClass())); - - if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(Class)) - { - return nullptr; - } - return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); -} diff --git a/Source/Editor/Private/Asset/SlotInfoFactory.h b/Source/Editor/Private/Asset/SlotInfoFactory.h deleted file mode 100644 index 60783ce..0000000 --- a/Source/Editor/Private/Asset/SlotInfoFactory.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once - -#include "SlotInfo.h" - -#include "AssetTypeActions_Base.h" -#include "Factories/Factory.h" - -#include "SlotInfoFactory.generated.h" - - -UCLASS() -class SAVEEXTENSIONEDITOR_API USlotInfoFactory : public UFactory { - GENERATED_BODY() - -public: - - USlotInfoFactory(); - - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; -}; diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.cpp b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.cpp index 5b273ea..f848237 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.cpp +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.cpp @@ -1,12 +1,14 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "ClassFilterHelpers.h" -#include + +#include "UnloadedBlueprintData.h" + +#include #include +#include #include -#include -#include "UnloadedBlueprintData.h" namespace ClassFilter @@ -19,8 +21,11 @@ namespace ClassFilter FClassHierarchy::FClassHierarchy() { // Register with the Asset Registry to be informed when it is done loading up files. - FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked(TEXT("AssetRegistry")); - OnFilesLoadedRequestPopulateClassHierarchyDelegateHandle = AssetRegistryModule.Get().OnFilesLoaded().AddStatic(ClassFilter::Helpers::RequestPopulateClassHierarchy); + FAssetRegistryModule& AssetRegistryModule = + FModuleManager::GetModuleChecked(TEXT("AssetRegistry")); + OnFilesLoadedRequestPopulateClassHierarchyDelegateHandle = + AssetRegistryModule.Get().OnFilesLoaded().AddStatic( + ClassFilter::Helpers::RequestPopulateClassHierarchy); AssetRegistryModule.Get().OnAssetAdded().AddRaw(this, &FClassHierarchy::AddAsset); AssetRegistryModule.Get().OnAssetRemoved().AddRaw(this, &FClassHierarchy::RemoveAsset); @@ -28,8 +33,11 @@ namespace ClassFilter FCoreUObjectDelegates::ReloadCompleteDelegate.AddRaw(this, &FClassHierarchy::OnReloadComplete); // Register to have Populate called when a Blueprint is compiled. - OnBlueprintCompiledRequestPopulateClassHierarchyDelegateHandle = GEditor->OnBlueprintCompiled().AddStatic(ClassFilter::Helpers::RequestPopulateClassHierarchy); - OnClassPackageLoadedOrUnloadedRequestPopulateClassHierarchyDelegateHandle = GEditor->OnClassPackageLoadedOrUnloaded().AddStatic(ClassFilter::Helpers::RequestPopulateClassHierarchy); + OnBlueprintCompiledRequestPopulateClassHierarchyDelegateHandle = + GEditor->OnBlueprintCompiled().AddStatic(ClassFilter::Helpers::RequestPopulateClassHierarchy); + OnClassPackageLoadedOrUnloadedRequestPopulateClassHierarchyDelegateHandle = + GEditor->OnClassPackageLoadedOrUnloaded().AddStatic( + ClassFilter::Helpers::RequestPopulateClassHierarchy); FModuleManager::Get().OnModulesChanged().AddStatic(&OnModulesChanged); } @@ -39,8 +47,10 @@ namespace ClassFilter // Unregister with the Asset Registry to be informed when it is done loading up files. if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry"))) { - FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked(TEXT("AssetRegistry")); - AssetRegistryModule.Get().OnFilesLoaded().Remove(OnFilesLoadedRequestPopulateClassHierarchyDelegateHandle); + FAssetRegistryModule& AssetRegistryModule = + FModuleManager::GetModuleChecked(TEXT("AssetRegistry")); + AssetRegistryModule.Get().OnFilesLoaded().Remove( + OnFilesLoadedRequestPopulateClassHierarchyDelegateHandle); AssetRegistryModule.Get().OnAssetAdded().RemoveAll(this); AssetRegistryModule.Get().OnAssetRemoved().RemoveAll(this); @@ -49,18 +59,23 @@ namespace ClassFilter if (GEditor) { // Unregister to have Populate called when a Blueprint is compiled. - GEditor->OnBlueprintCompiled().Remove(OnBlueprintCompiledRequestPopulateClassHierarchyDelegateHandle); - GEditor->OnClassPackageLoadedOrUnloaded().Remove(OnClassPackageLoadedOrUnloadedRequestPopulateClassHierarchyDelegateHandle); + GEditor->OnBlueprintCompiled().Remove( + OnBlueprintCompiledRequestPopulateClassHierarchyDelegateHandle); + GEditor->OnClassPackageLoadedOrUnloaded().Remove( + OnClassPackageLoadedOrUnloadedRequestPopulateClassHierarchyDelegateHandle); } } FModuleManager::Get().OnModulesChanged().RemoveAll(this); } - static FSEClassFilterNodePtr CreateNodeForClass(UClass* Class, const TMultiMap& BlueprintPackageToAssetDataMap) + static FSEClassFilterNodePtr CreateNodeForClass( + UClass* Class, const TMultiMap& BlueprintPackageToAssetDataMap) { - // Create the new node so it can be passed to AddChildren, fill it in with if it is placeable, abstract, and/or a brush. - TSharedPtr NewNode = MakeShared(Class->GetName(), Class->GetDisplayNameText().ToString()); + // Create the new node so it can be passed to AddChildren, fill it in with if it is placeable, + // abstract, and/or a brush. + TSharedPtr NewNode = + MakeShared(Class->GetName(), Class->GetDisplayNameText().ToString()); NewNode->Blueprint = ClassFilter::Helpers::GetBlueprint(Class); NewNode->Class = Class; NewNode->ClassPath = Class->GetClassPathName(); @@ -77,11 +92,13 @@ namespace ClassFilter ClassFilter::Helpers::RequestPopulateClassHierarchy(); } - void FClassHierarchy::AddChildren_NoFilter(FSEClassFilterNodePtr& InOutRootNode, const TMultiMap& BlueprintPackageToAssetDataMap) + void FClassHierarchy::AddChildren_NoFilter(FSEClassFilterNodePtr& InOutRootNode, + const TMultiMap& BlueprintPackageToAssetDataMap) { UClass* RootClass = UObject::StaticClass(); - ObjectClassRoot = MakeShared(RootClass->GetName(), RootClass->GetDisplayNameText().ToString()); + ObjectClassRoot = + MakeShared(RootClass->GetName(), RootClass->GetDisplayNameText().ToString()); ObjectClassRoot->Class = RootClass; TMap Nodes; @@ -115,7 +132,8 @@ namespace ClassFilter FSEClassFilterNodePtr& ParentEntry = Nodes.FindOrAdd(CurrentClass->GetSuperClass()); if (!ParentEntry.IsValid()) { - ParentEntry = CreateNodeForClass(CurrentClass->GetSuperClass(), BlueprintPackageToAssetDataMap); + ParentEntry = + CreateNodeForClass(CurrentClass->GetSuperClass(), BlueprintPackageToAssetDataMap); } FSEClassFilterNodePtr& MyEntry = Nodes.FindOrAdd(CurrentClass); @@ -136,7 +154,8 @@ namespace ClassFilter } } - FSEClassFilterNodePtr FClassHierarchy::FindParent(const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InParentClassPath, const UClass* InParentClass) + FSEClassFilterNodePtr FClassHierarchy::FindParent(const FSEClassFilterNodePtr& InRootNode, + FTopLevelAssetPath InParentClassPath, const UClass* InParentClass) { // Check if the current node is the parent class name that is being searched for. if (InRootNode->ClassPath == InParentClassPath) @@ -149,21 +168,21 @@ namespace ClassFilter // If a class does not have a generated class name, we look up the parent class and compare. const UClass* ParentClass = InParentClass; - if (const UClass * RootClass = InRootNode->Class.Get()) + if (const UClass* RootClass = InRootNode->Class.Get()) { if (ParentClass == RootClass) { return InRootNode; } } - } // Search the children recursively, one of them might have the parent. FSEClassFilterNodePtr ReturnNode; for (const auto& Child : InRootNode->GetChildrenList()) { - // Check the child, then check the return to see if it is valid. If it is valid, end the recursion. + // Check the child, then check the return to see if it is valid. If it is valid, end the + // recursion. ReturnNode = FindParent(Child, InParentClassPath, InParentClass); if (ReturnNode.IsValid()) { @@ -173,7 +192,8 @@ namespace ClassFilter return {}; } - FSEClassFilterNodePtr FClassHierarchy::FindNodeByClassName(const FSEClassFilterNodePtr& InRootNode, const FString& InClassName) + FSEClassFilterNodePtr FClassHierarchy::FindNodeByClassName( + const FSEClassFilterNodePtr& InRootNode, const FString& InClassName) { FString NodeClassName = InRootNode->Class.IsValid() ? InRootNode->Class->GetPathName() : FString(); if (NodeClassName == InClassName) @@ -185,7 +205,8 @@ namespace ClassFilter FSEClassFilterNodePtr ReturnNode; for (const auto& Child : InRootNode->GetChildrenList()) { - // Check the child, then check the return to see if it is valid. If it is valid, end the recursion. + // Check the child, then check the return to see if it is valid. If it is valid, end the + // recursion. ReturnNode = FindNodeByClassName(Child, InClassName); if (ReturnNode.IsValid()) { @@ -195,7 +216,8 @@ namespace ClassFilter return {}; } - FSEClassFilterNodePtr FClassHierarchy::FindNodeByClass(const FSEClassFilterNodePtr& InRootNode, const UClass* Class) + FSEClassFilterNodePtr FClassHierarchy::FindNodeByClass( + const FSEClassFilterNodePtr& InRootNode, const UClass* Class) { if (InRootNode->Class.IsValid() && InRootNode->Class == Class) { @@ -206,7 +228,8 @@ namespace ClassFilter FSEClassFilterNodePtr ReturnNode; for (const auto& Child : InRootNode->GetChildrenList()) { - // Check the child, then check the return to see if it is valid. If it is valid, end the recursion. + // Check the child, then check the return to see if it is valid. If it is valid, end the + // recursion. ReturnNode = FindNodeByClass(Child, Class); if (ReturnNode.IsValid()) { @@ -216,7 +239,8 @@ namespace ClassFilter return {}; } - FSEClassFilterNodePtr FClassHierarchy::FindNodeByGeneratedClassPath(const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InGeneratedClassPath) + FSEClassFilterNodePtr FClassHierarchy::FindNodeByGeneratedClassPath( + const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InGeneratedClassPath) { if (InRootNode->ClassPath == InGeneratedClassPath) { @@ -227,7 +251,8 @@ namespace ClassFilter FSEClassFilterNodePtr ReturnNode; for (const auto& Child : InRootNode->GetChildrenList()) { - // Check the child, then check the return to see if it is valid. If it is valid, end the recursion. + // Check the child, then check the return to see if it is valid. If it is valid, end the + // recursion. ReturnNode = FindNodeByGeneratedClassPath(Child, InGeneratedClassPath); if (ReturnNode.IsValid()) { @@ -237,7 +262,8 @@ namespace ClassFilter return {}; } - void FClassHierarchy::UpdateClassInNode(FTopLevelAssetPath InGeneratedClassPath, UClass* InNewClass, UBlueprint* InNewBluePrint) + void FClassHierarchy::UpdateClassInNode( + FTopLevelAssetPath InGeneratedClassPath, UClass* InNewClass, UBlueprint* InNewBluePrint) { FSEClassFilterNodePtr Node = FindNodeByGeneratedClassPath(ObjectClassRoot, InGeneratedClassPath); @@ -248,7 +274,8 @@ namespace ClassFilter } } - bool FClassHierarchy::FindAndRemoveNodeByClassPath(const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InClassPath) + bool FClassHierarchy::FindAndRemoveNodeByClassPath( + const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InClassPath) { bool bReturnValue = false; @@ -263,7 +290,8 @@ namespace ClassFilter return true; } - // Check the child, then check the return to see if it is valid. If it is valid, end the recursion. + // Check the child, then check the return to see if it is valid. If it is valid, end the + // recursion. bReturnValue |= FindAndRemoveNodeByClassPath(Child, InClassPath); if (bReturnValue) { @@ -290,11 +318,13 @@ namespace ClassFilter void FClassHierarchy::AddAsset(const FAssetData& InAddedAssetData) { - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + FAssetRegistryModule& AssetRegistryModule = + FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); if (!AssetRegistryModule.Get().IsLoadingAssets()) { TArray AncestorClassPaths; - AssetRegistryModule.Get().GetAncestorClassNames(InAddedAssetData.AssetClassPath, AncestorClassPaths); + AssetRegistryModule.Get().GetAncestorClassNames( + InAddedAssetData.AssetClassPath, AncestorClassPaths); if (AncestorClassPaths.Contains(UBlueprintCore::StaticClass()->GetClassPathName())) { @@ -304,7 +334,8 @@ namespace ClassFilter ClassObjectPath = FPackageName::ExportTextPathToObjectPath(ClassObjectPath); } - // Make sure that the node does not already exist. There is a bit of double adding going on at times and this prevents it. + // Make sure that the node does not already exist. There is a bit of double adding going on at + // times and this prevents it. if (!FindNodeByGeneratedClassPath(ObjectClassRoot, FTopLevelAssetPath{ClassObjectPath}) .IsValid()) { @@ -317,7 +348,8 @@ namespace ClassFilter // Resolve the parent's class name locally and use it to find the parent's class. FString ParentClassPath = NewNode->ParentClassPath.ToString(); UClass* ParentClass = FindObject(nullptr, *ParentClassPath); - FSEClassFilterNodePtr ParentNode = FindParent(ObjectClassRoot, NewNode->ParentClassPath, ParentClass); + FSEClassFilterNodePtr ParentNode = + FindParent(ObjectClassRoot, NewNode->ParentClassPath, ParentClass); if (ParentNode.IsValid()) { ParentNode->AddChild(NewNode); @@ -347,13 +379,14 @@ namespace ClassFilter void FClassHierarchy::SortChildren(FSEClassFilterNodePtr& InRootNode) { - TArray< FSEClassFilterNodePtr >& ChildList = InRootNode->GetChildrenList(); + TArray& ChildList = InRootNode->GetChildrenList(); for (auto& Child : InRootNode->GetChildrenList()) { // Setup the parent weak pointer, useful for going up the tree for unloaded blueprints. Child->ParentNode = InRootNode; - // Check the child, then check the return to see if it is valid. If it is valid, end the recursion. + // Check the child, then check the return to see if it is valid. If it is valid, end the + // recursion. SortChildren(Child); } @@ -372,7 +405,8 @@ namespace ClassFilter } } - void FClassHierarchy::LoadUnloadedTagData(FSEClassFilterNodePtr& InOutClassFilterNode, const FAssetData& InAssetData) + void FClassHierarchy::LoadUnloadedTagData( + FSEClassFilterNodePtr& InOutClassFilterNode, const FAssetData& InAssetData) { const FString ClassName = InAssetData.AssetName.ToString(); FString ClassDisplayName = InAssetData.GetTagValueRef(FBlueprintTags::BlueprintDisplayName); @@ -394,20 +428,24 @@ namespace ClassFilter FString ParentClassPathString; if (InAssetData.GetTagValue(FBlueprintTags::ParentClassPath, ParentClassPathString)) { - InOutClassFilterNode->ParentClassPath = FPackageName::ExportTextPathToObjectPath(ParentClassPathString); + InOutClassFilterNode->ParentClassPath = + FPackageName::ExportTextPathToObjectPath(ParentClassPathString); } - InOutClassFilterNode->bIsBPNormalType = InAssetData.GetTagValueRef(FBlueprintTags::BlueprintType) == TEXT("BPType_Normal"); + InOutClassFilterNode->bIsBPNormalType = + InAssetData.GetTagValueRef(FBlueprintTags::BlueprintType) == TEXT("BPType_Normal"); // It is an unloaded blueprint, so we need to create the structure that will hold the data. - TSharedPtr UnloadedBlueprintData = MakeShared(InOutClassFilterNode); + TSharedPtr UnloadedBlueprintData = + MakeShared(InOutClassFilterNode); InOutClassFilterNode->UnloadedBlueprintData = UnloadedBlueprintData; // Get the class flags. const uint32 ClassFlags = InAssetData.GetTagValueRef(FBlueprintTags::ClassFlags); InOutClassFilterNode->UnloadedBlueprintData->SetClassFlags(ClassFlags); - const FString ImplementedInterfaces = InAssetData.GetTagValueRef(FBlueprintTags::ImplementedInterfaces); + const FString ImplementedInterfaces = + InAssetData.GetTagValueRef(FBlueprintTags::ImplementedInterfaces); if (!ImplementedInterfaces.IsEmpty()) { FString FullInterface; @@ -418,13 +456,16 @@ namespace ClassFilter { if (!CurrentString.StartsWith(TEXT("Graphs=("))) { - if (FullInterface.Split(TEXT("\""), &CurrentString, &InterfacePath, ESearchCase::CaseSensitive)) + if (FullInterface.Split( + TEXT("\""), &CurrentString, &InterfacePath, ESearchCase::CaseSensitive)) { // The interface paths in metadata end with "', so remove those InterfacePath.RemoveFromEnd(TEXT("\"'")); - FCoreRedirectObjectName ResolvedInterfaceName = FCoreRedirects::GetRedirectedName(ECoreRedirectFlags::Type_Class, FCoreRedirectObjectName(InterfacePath)); - UnloadedBlueprintData->AddImplementedInterface(ResolvedInterfaceName.ObjectName.ToString()); + FCoreRedirectObjectName ResolvedInterfaceName = FCoreRedirects::GetRedirectedName( + ECoreRedirectFlags::Type_Class, FCoreRedirectObjectName(InterfacePath)); + UnloadedBlueprintData->AddImplementedInterface( + ResolvedInterfaceName.ObjectName.ToString()); } } @@ -435,9 +476,10 @@ namespace ClassFilter void FClassHierarchy::PopulateClassHierarchy() { - TArray< FSEClassFilterNodePtr > RootLevelClasses; + TArray RootLevelClasses; - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + FAssetRegistryModule& AssetRegistryModule = + FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); // Retrieve all blueprint classes TArray BlueprintList; @@ -447,7 +489,8 @@ namespace ClassFilter Filter.ClassPaths.Add(UAnimBlueprint::StaticClass()->GetClassPathName()); Filter.ClassPaths.Add(UBlueprintGeneratedClass::StaticClass()->GetClassPathName()); - // Include any Blueprint based objects as well, this includes things like Blutilities, UMG, and GameplayAbility objects + // Include any Blueprint based objects as well, this includes things like Blutilities, UMG, and + // GameplayAbility objects Filter.bRecursiveClasses = true; AssetRegistryModule.Get().GetAssets(Filter, BlueprintList); @@ -480,17 +523,18 @@ namespace ClassFilter for (int32 SearchNodeIdx = 0; SearchNodeIdx < RootLevelClasses.Num(); ++SearchNodeIdx) { - FSEClassFilterNodePtr ParentNode = FindParent(RootLevelClasses[SearchNodeIdx], RootLevelClasses[CurrentNodeIdx]->ParentClassPath, ParentClass); + FSEClassFilterNodePtr ParentNode = FindParent(RootLevelClasses[SearchNodeIdx], + RootLevelClasses[CurrentNodeIdx]->ParentClassPath, ParentClass); if (ParentNode.IsValid()) { - // AddUniqueChild makes sure that when a node was generated one by EditorClassHierarchy and one from LoadUnloadedTagData - the proper one is selected + // AddUniqueChild makes sure that when a node was generated one by + // EditorClassHierarchy and one from LoadUnloadedTagData - the proper one is selected ParentNode->AddUniqueChild(RootLevelClasses[CurrentNodeIdx]); RootLevelClasses.RemoveAtSwap(CurrentNodeIdx); --CurrentNodeIdx; break; } } - } } @@ -500,4 +544,4 @@ namespace ClassFilter // All viewers must refresh. ClassFilter::Helpers::RefreshAll(); } -} +} // namespace ClassFilter diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h index 0468dcf..41ac0ca 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h @@ -1,26 +1,28 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ClassFilterNode.h" +#include "Misc/ClassFilter.h" + #include -#include -#include -#include -#include -#include #include -#include -#include #include -#include #include +#include +#include +#include +#include +#include #include +#include +#include #include -#include -#include -#include #include -#include "Misc/ClassFilter.h" +#include +#include +#include +#include + #define LOCTEXT_NAMESPACE "ClassFilterHelpers" @@ -47,7 +49,8 @@ namespace ClassFilter FClassHierarchy(); ~FClassHierarchy(); - /** Populates the class hierarchy tree, pulling all the loaded and unloaded classes into a master tree. */ + /** Populates the class hierarchy tree, pulling all the loaded and unloaded classes into a master + * tree. */ void PopulateClassHierarchy(); /** Recursive function to sort a tree. @@ -63,7 +66,7 @@ namespace ClassFilter // This node should always be valid. check(ObjectClassRoot.IsValid()) - return ObjectClassRoot; + return ObjectClassRoot; } /** Finds the parent of a node, recursively going deeper into the hierarchy. @@ -79,40 +82,47 @@ namespace ClassFilter /** Updates the Class of a node. Uses the generated class package name to find the node. * @param InGeneratedClassPath The path of the generated class to find the node for. * @param InNewClass The class to update the node with. - */ - void UpdateClassInNode(FTopLevelAssetPath InGeneratedClassPath, UClass* InNewClass, UBlueprint* InNewBluePrint); + */ + void UpdateClassInNode( + FTopLevelAssetPath InGeneratedClassPath, UClass* InNewClass, UBlueprint* InNewBluePrint); /** Finds the node, recursively going deeper into the hierarchy. Does so by comparing class names. - * @param InClassName The name of the generated class package to find the node for. - * - * @return The node. - */ - FSEClassFilterNodePtr FindNodeByClassName(const FSEClassFilterNodePtr& InRootNode, const FString& InClassName); + * @param InClassName The name of the generated class package to find the node for. + * + * @return The node. + */ + FSEClassFilterNodePtr FindNodeByClassName( + const FSEClassFilterNodePtr& InRootNode, const FString& InClassName); /** Finds the node, recursively going deeper into the hierarchy. Does so by comparing class names. - * @param InClass The pointer of the class to find the node for. - * - * @return The node. - */ + * @param InClass The pointer of the class to find the node for. + * + * @return The node. + */ FSEClassFilterNodePtr FindNodeByClass(const FSEClassFilterNodePtr& InRootNode, const UClass* Class); private: /** Recursive function to build a tree, will not filter. - * @param InOutRootNode The node that this function will add the children of to the tree. - * @param PackageNameToAssetDataMap The asset registry map of blueprint package names to blueprint data + * @param InOutRootNode The node that this function will add the children of to the + *tree. + * @param PackageNameToAssetDataMap The asset registry map of blueprint package names to + *blueprint data */ - void AddChildren_NoFilter(FSEClassFilterNodePtr& InOutRootNode, const TMultiMap& BlueprintPackageToAssetDataMap); + void AddChildren_NoFilter(FSEClassFilterNodePtr& InOutRootNode, + const TMultiMap& BlueprintPackageToAssetDataMap); /** Called when hot reload has finished */ void OnReloadComplete(EReloadCompleteReason Reason); - /** Finds the node, recursively going deeper into the hierarchy. Does so by comparing generated class package names. + /** Finds the node, recursively going deeper into the hierarchy. Does so by comparing generated class + *package names. * @param InGeneratedClassPath The path of the generated class to find the node for. * * @return The node. */ - FSEClassFilterNodePtr FindNodeByGeneratedClassPath(const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InGeneratedClassPath); + FSEClassFilterNodePtr FindNodeByGeneratedClassPath( + const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InGeneratedClassPath); /** * Loads the tag data for an unloaded blueprint asset. @@ -137,7 +147,8 @@ namespace ClassFilter * * @return Returns true if the asset was found and deleted successfully. */ - bool FindAndRemoveNodeByClassPath(const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InClassPath); + bool FindAndRemoveNodeByClassPath( + const FSEClassFilterNodePtr& InRootNode, FTopLevelAssetPath InClassPath); /** Callback registered to the Asset Registry to be notified when an asset is added. */ void AddAsset(const FAssetData& InAddedAssetData); @@ -149,10 +160,10 @@ namespace ClassFilter namespace Helpers { - DECLARE_MULTICAST_DELEGATE( FPopulateClassFilter ); + DECLARE_MULTICAST_DELEGATE(FPopulateClassFilter); /** The class hierarchy that manages the unfiltered class tree for the Class Viewer. */ - static TSharedPtr< FClassHierarchy > ClassHierarchy; + static TSharedPtr ClassHierarchy; /** Used to inform any registered Class Viewers to refresh. */ static FPopulateClassFilter PopulateClassFilterDelegate; @@ -161,8 +172,8 @@ namespace ClassFilter static bool bPopulateClassHierarchy; // Pre-declare these functions. - static bool CheckIfBlueprintBase( FSEClassFilterNodePtr InNode ); - static UBlueprint* GetBlueprint( UClass* InClass ); + static bool CheckIfBlueprintBase(FSEClassFilterNodePtr InNode); + static UBlueprint* GetBlueprint(UClass* InClass); static void UpdateClassInNode( FTopLevelAssetPath InGeneratedClassPath, UClass* InNewClass, UBlueprint* InNewBluePrint); @@ -177,10 +188,10 @@ namespace ClassFilter bool bIsClassDeprecated = InClass->HasAnyClassFlags(CLASS_Deprecated); InClass->ClassFlags &= ~CLASS_Deprecated; - bool bCanCreateBlueprintOfClass = FKismetEditorUtilities::CanCreateBlueprintOfClass( InClass ); + bool bCanCreateBlueprintOfClass = FKismetEditorUtilities::CanCreateBlueprintOfClass(InClass); // Reassign the deprecated flag if it was previously assigned - if(bIsClassDeprecated) + if (bIsClassDeprecated) { InClass->ClassFlags |= CLASS_Deprecated; } @@ -210,12 +221,12 @@ namespace ClassFilter /** Will create the instance of FClassHierarchy and populate the class hierarchy tree. */ static void ConstructClassHierarchy() { - if(!ClassHierarchy.IsValid()) + if (!ClassHierarchy.IsValid()) { ClassHierarchy = MakeShared(); // When created, populate the hierarchy. - GWarn->BeginSlowTask( LOCTEXT("RebuildingClassHierarchy", "Rebuilding Class Hierarchy"), true ); + GWarn->BeginSlowTask(LOCTEXT("RebuildingClassHierarchy", "Rebuilding Class Hierarchy"), true); ClassHierarchy->PopulateClassHierarchy(); GWarn->EndSlowTask(); } @@ -230,11 +241,11 @@ namespace ClassFilter /** Will populate the class hierarchy tree if previously requested. */ static void PopulateClassHierarchy() { - if(bPopulateClassHierarchy) + if (bPopulateClassHierarchy) { bPopulateClassHierarchy = false; - GWarn->BeginSlowTask( LOCTEXT("RebuildingClassHierarchy", "Rebuilding Class Hierarchy"), true ); + GWarn->BeginSlowTask(LOCTEXT("RebuildingClassHierarchy", "Rebuilding Class Hierarchy"), true); ClassHierarchy->PopulateClassHierarchy(); GWarn->EndSlowTask(); } @@ -252,12 +263,16 @@ namespace ClassFilter ClassFilter::Helpers::PopulateClassFilterDelegate.Broadcast(); } - /** Recursive function to build a tree, filtering out nodes based on the InitOptions and filter search terms. - * @param InInitOptions The class viewer's options, holds the AllowedClasses and DisallowedClasses. - * @param InOutRootNode The node that this function will add the children of to the tree. + /** Recursive function to build a tree, filtering out nodes based on the InitOptions and filter search + *terms. + * @param InInitOptions The class viewer's options, holds the AllowedClasses and + *DisallowedClasses. + * @param InOutRootNode The node that this function will add the children of to the + *tree. * @param InRootClassIndex The index of the root node. * @param bInOnlyBlueprintBases Filter option to remove non-blueprint base classes. - * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class filter options. + * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class + *filter options. * @param bInInternalClasses Filter option for showing internal classes. * @param InternalClasses The classes that have been marked as Internal Only. * @param InternalPaths The paths that have been marked Internal Only. @@ -265,20 +280,21 @@ namespace ClassFilter * @return Returns true if the child passed the filter. */ static bool AddChildren_Tree(const FSEClassFilter& Filter, FSEClassFilterNodePtr& InOutRootNode, - const FSEClassFilterNodePtr& InOriginalRootNode, - bool bInOnlyBlueprintBases, bool bInShowUnloadedBlueprints, bool bInInternalClasses, - const TArray& InternalClasses, const TArray& InternalPaths) + const FSEClassFilterNodePtr& InOriginalRootNode, bool bInOnlyBlueprintBases, + bool bInShowUnloadedBlueprints, bool bInInternalClasses, const TArray& InternalClasses, + const TArray& InternalPaths) { - bool bChildrenPassesFilter= false; + bool bChildrenPassesFilter = false; bool bReturnPassesFilter = false; - bool bPassesBlueprintBaseFilter = !bInOnlyBlueprintBases || CheckIfBlueprintBase(InOriginalRootNode); + bool bPassesBlueprintBaseFilter = + !bInOnlyBlueprintBases || CheckIfBlueprintBase(InOriginalRootNode); bool bIsUnloadedBlueprint = !InOriginalRootNode->Class.IsValid(); FString GeneratedClassPathString = InOriginalRootNode->ClassPath.ToString(); - // The INI files declare classes and folders that are considered internal only. Does this class match any of those patterns? - // INI path: /Script/ClassFilter.ClassFilterProjectSettings + // The INI files declare classes and folders that are considered internal only. Does this class + // match any of those patterns? INI path: /Script/ClassFilter.ClassFilterProjectSettings bool bPassesInternalFilter = true; if (!bInInternalClasses && InternalPaths.Num() > 0) { @@ -291,7 +307,8 @@ namespace ClassFilter } } } - if (!bInInternalClasses && InternalClasses.Num() > 0 && bPassesInternalFilter && InOriginalRootNode->Class.IsValid()) + if (!bInInternalClasses && InternalClasses.Num() > 0 && bPassesInternalFilter && + InOriginalRootNode->Class.IsValid()) { for (int i = 0; i < InternalClasses.Num(); i++) { @@ -303,27 +320,33 @@ namespace ClassFilter } } - // There are few options for filtering an unloaded blueprint, if it matches with this filter, it passes. - if(bIsUnloadedBlueprint) + // There are few options for filtering an unloaded blueprint, if it matches with this filter, it + // passes. + if (bIsUnloadedBlueprint) { - if(bInShowUnloadedBlueprints) + if (bInShowUnloadedBlueprints) { - bReturnPassesFilter = InOutRootNode->bPassesFilter = bPassesBlueprintBaseFilter && bPassesInternalFilter && PassesFilter(*InOriginalRootNode->GetClassName()); + bReturnPassesFilter = InOutRootNode->bPassesFilter = + bPassesBlueprintBaseFilter && bPassesInternalFilter && + PassesFilter(*InOriginalRootNode->GetClassName()); } } else { - bReturnPassesFilter = InOutRootNode->bPassesFilter = bPassesBlueprintBaseFilter && bPassesInternalFilter && PassesFilter(*InOriginalRootNode->GetClassName()); + bReturnPassesFilter = InOutRootNode->bPassesFilter = + bPassesBlueprintBaseFilter && bPassesInternalFilter && + PassesFilter(*InOriginalRootNode->GetClassName()); } - for(const auto& Child : InOriginalRootNode->GetChildrenList()) + for (const auto& Child : InOriginalRootNode->GetChildrenList()) { - FSEClassFilterNodePtr NewNode = MakeShared( *Child.Get() ); + FSEClassFilterNodePtr NewNode = MakeShared(*Child.Get()); NewNode->SetStateFromFilter(Filter); - bChildrenPassesFilter = AddChildren_Tree(Filter, NewNode, Child, bInOnlyBlueprintBases, bInShowUnloadedBlueprints, bInInternalClasses, InternalClasses, InternalPaths); - if(bChildrenPassesFilter) + bChildrenPassesFilter = AddChildren_Tree(Filter, NewNode, Child, bInOnlyBlueprintBases, + bInShowUnloadedBlueprints, bInInternalClasses, InternalClasses, InternalPaths); + if (bChildrenPassesFilter) { InOutRootNode->AddChild(NewNode); } @@ -333,26 +356,30 @@ namespace ClassFilter } /** Builds the class tree. - * @param InInitOptions The class viewer's options, holds the AllowedClasses and DisallowedClasses. + * @param InInitOptions The class viewer's options, holds the AllowedClasses and + *DisallowedClasses. * @param InOutRootNode The node to root the tree to. * @param bInOnlyBlueprintBases Filter option to remove non-blueprint base classes. - * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class filter options. + * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class + *filter options. * @param bInInternalClasses Filter option for showing internal classes. * @param InternalClasses The classes that have been marked as Internal Only. * @param InternalPaths The paths that have been marked Internal Only. * @return A fully built tree. */ - static void GetClassTree(const FSEClassFilter& Filter, FSEClassFilterNodePtr& InOutRootNode, bool bInOnlyBlueprintBases, - bool bInShowUnloadedBlueprints, bool bInInternalClasses = true, - const TArray& InternalClasses = TArray(), const TArray& InternalPaths = TArray()) + static void GetClassTree(const FSEClassFilter& Filter, FSEClassFilterNodePtr& InOutRootNode, + bool bInOnlyBlueprintBases, bool bInShowUnloadedBlueprints, bool bInInternalClasses = true, + const TArray& InternalClasses = TArray(), + const TArray& InternalPaths = TArray()) { // Use BaseClass as root FSEClassFilterNodePtr RootNode; if (Filter.GetBaseClass()) { - RootNode = ClassHierarchy->FindNodeByClass(ClassHierarchy->GetObjectRootNode(), Filter.GetBaseClass()); + RootNode = ClassHierarchy->FindNodeByClass( + ClassHierarchy->GetObjectRootNode(), Filter.GetBaseClass()); } - else // Use UObject as root + else // Use UObject as root { RootNode = ClassHierarchy->GetObjectRootNode(); } @@ -360,34 +387,39 @@ namespace ClassFilter // Duplicate the node, it will have no children. InOutRootNode = MakeShared(*RootNode); - AddChildren_Tree(Filter, InOutRootNode, RootNode, bInOnlyBlueprintBases, bInShowUnloadedBlueprints, bInInternalClasses, InternalClasses, InternalPaths); + AddChildren_Tree(Filter, InOutRootNode, RootNode, bInOnlyBlueprintBases, + bInShowUnloadedBlueprints, bInInternalClasses, InternalClasses, InternalPaths); } - /** Recursive function to build the list, filtering out nodes based on the InitOptions and filter search terms. - * @param InInitOptions The class viewer's options, holds the AllowedClasses and DisallowedClasses. - * @param InOutRootNode The node that this function will add the children of to the tree. + /** Recursive function to build the list, filtering out nodes based on the InitOptions and filter + *search terms. + * @param InInitOptions The class viewer's options, holds the AllowedClasses and + *DisallowedClasses. + * @param InOutRootNode The node that this function will add the children of to the + *tree. * @param InRootClassIndex The index of the root node. * @param bInOnlyBlueprintBases Filter option to remove non-blueprint base classes. - * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class filter options. + * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class + *filter options. * @param bInInternalClasses Filter option for showing internal classes. * @param InternalClasses The classes that have been marked as Internal Only. * @param InternalPaths The paths that have been marked Internal Only. * * @return Returns true if the child passed the filter. */ - static void AddChildren_List(TArray< FSEClassFilterNodePtr >& InOutNodeList, - const FSEClassFilterNodePtr& InOriginalRootNode, - bool bInOnlyBlueprintBases, bool bInShowUnloadedBlueprints, - bool bInInternalClasses, - const TArray& InternalClasses, const TArray& InternalPaths) + static void AddChildren_List(TArray& InOutNodeList, + const FSEClassFilterNodePtr& InOriginalRootNode, bool bInOnlyBlueprintBases, + bool bInShowUnloadedBlueprints, bool bInInternalClasses, const TArray& InternalClasses, + const TArray& InternalPaths) { - bool bPassesBlueprintBaseFilter = !bInOnlyBlueprintBases || CheckIfBlueprintBase(InOriginalRootNode); + bool bPassesBlueprintBaseFilter = + !bInOnlyBlueprintBases || CheckIfBlueprintBase(InOriginalRootNode); bool bIsUnloadedBlueprint = !InOriginalRootNode->Class.IsValid(); FString GeneratedClassPathString = InOriginalRootNode->ClassPath.ToString(); - // The INI files declare classes and folders that are considered internal only. Does this class match any of those patterns? - // INI path: /Script/ClassFilter.ClassFilterProjectSettings + // The INI files declare classes and folders that are considered internal only. Does this class + // match any of those patterns? INI path: /Script/ClassFilter.ClassFilterProjectSettings bool bPassesInternalFilter = true; if (!bInInternalClasses && InternalPaths.Num() > 0) { @@ -414,52 +446,58 @@ namespace ClassFilter FSEClassFilterNodePtr NewNode = MakeShared(*InOriginalRootNode.Get()); - // There are few options for filtering an unloaded blueprint, if it matches with this filter, it passes. - if(bIsUnloadedBlueprint) + // There are few options for filtering an unloaded blueprint, if it matches with this filter, it + // passes. + if (bIsUnloadedBlueprint) { - if(bInShowUnloadedBlueprints) + if (bInShowUnloadedBlueprints) { - NewNode->bPassesFilter = bPassesBlueprintBaseFilter && bPassesInternalFilter && PassesFilter(*InOriginalRootNode->GetClassName()); + NewNode->bPassesFilter = bPassesBlueprintBaseFilter && bPassesInternalFilter && + PassesFilter(*InOriginalRootNode->GetClassName()); } } else { - NewNode->bPassesFilter = bPassesBlueprintBaseFilter && bPassesInternalFilter && PassesFilter(*InOriginalRootNode->GetClassName()); + NewNode->bPassesFilter = bPassesBlueprintBaseFilter && bPassesInternalFilter && + PassesFilter(*InOriginalRootNode->GetClassName()); } - if(NewNode->bPassesFilter) + if (NewNode->bPassesFilter) { InOutNodeList.Add(NewNode); } for (const auto& Child : InOriginalRootNode->GetChildrenList()) { - AddChildren_List(InOutNodeList, Child, bInOnlyBlueprintBases, - bInShowUnloadedBlueprints, bInInternalClasses, InternalClasses, InternalPaths); + AddChildren_List(InOutNodeList, Child, bInOnlyBlueprintBases, bInShowUnloadedBlueprints, + bInInternalClasses, InternalClasses, InternalPaths); } } /** Builds the class list. - * @param InInitOptions The class viewer's options, holds the AllowedClasses and DisallowedClasses. + * @param InInitOptions The class viewer's options, holds the AllowedClasses and + *DisallowedClasses. * @param InOutNodeList The list to add all the nodes to. * @param bInOnlyBlueprintBases Filter option to remove non-blueprint base classes. - * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class filter options. + * @param bInShowUnloadedBlueprints Filter option to not remove unloaded blueprints due to class + *filter options. * @param bInInternalClasses Filter option for showing internal classes. * @param InternalClasses The classes that have been marked as Internal Only. * @param InternalPaths The paths that have been marked Internal Only. * * @return A fully built list. */ - static void GetClassList(TArray< FSEClassFilterNodePtr >& InOutNodeList, - bool bInOnlyBlueprintBases, bool bInShowUnloadedBlueprints, - bool bInInternalClasses = true, - const TArray& InternalClasses = TArray(), const TArray& InternalPaths = TArray()) + static void GetClassList(TArray& InOutNodeList, bool bInOnlyBlueprintBases, + bool bInShowUnloadedBlueprints, bool bInInternalClasses = true, + const TArray& InternalClasses = TArray(), + const TArray& InternalPaths = TArray()) { const FSEClassFilterNodePtr ObjectClassRoot = ClassHierarchy->GetObjectRootNode(); for (const auto& Child : ObjectClassRoot->GetChildrenList()) { - AddChildren_List(InOutNodeList, Child, bInOnlyBlueprintBases, bInShowUnloadedBlueprints, bInInternalClasses, InternalClasses, InternalPaths); + AddChildren_List(InOutNodeList, Child, bInOnlyBlueprintBases, bInShowUnloadedBlueprints, + bInInternalClasses, InternalClasses, InternalPaths); } } @@ -468,9 +506,9 @@ namespace ClassFilter * * @return The blueprint associated with the class index. */ - static UBlueprint* GetBlueprint( UClass* InClass ) + static UBlueprint* GetBlueprint(UClass* InClass) { - if( InClass->ClassGeneratedBy && InClass->ClassGeneratedBy->IsA(UBlueprint::StaticClass()) ) + if (InClass->ClassGeneratedBy && InClass->ClassGeneratedBy->IsA(UBlueprint::StaticClass())) { return Cast(InClass->ClassGeneratedBy); } @@ -485,11 +523,12 @@ namespace ClassFilter * * @return The blueprint associated with the class index. */ - static void GetClassInfo( TWeakObjectPtr InClass, bool& bInOutIsBlueprintBase, bool& bInOutHasBlueprint ) + static void GetClassInfo( + TWeakObjectPtr InClass, bool& bInOutIsBlueprintBase, bool& bInOutHasBlueprint) { if (UClass* Class = InClass.Get()) { - bInOutIsBlueprintBase = CanCreateBlueprintOfClass_IgnoreDeprecation( Class ); + bInOutIsBlueprintBase = CanCreateBlueprintOfClass_IgnoreDeprecation(Class); bInOutHasBlueprint = Class->ClassGeneratedBy != nullptr; } else @@ -504,17 +543,18 @@ namespace ClassFilter * * @return true if the class is a blueprint. */ - static bool CheckIfBlueprintBase( TSharedPtr< FSEClassFilterNode> InNode ) + static bool CheckIfBlueprintBase(TSharedPtr InNode) { // If there is no class, it may be an unloaded blueprint. - if(UClass* Class = InNode->Class.Get()) + if (UClass* Class = InNode->Class.Get()) { return CanCreateBlueprintOfClass_IgnoreDeprecation(Class); } - else if(InNode->bIsBPNormalType) + else if (InNode->bIsBPNormalType) { bool bAllowDerivedBlueprints = false; - GConfig->GetBool(TEXT("Kismet"), TEXT("AllowDerivedBlueprints"), /*out*/ bAllowDerivedBlueprints, GEngineIni); + GConfig->GetBool(TEXT("Kismet"), TEXT("AllowDerivedBlueprints"), + /*out*/ bAllowDerivedBlueprints, GEngineIni); return bAllowDerivedBlueprints; } @@ -525,7 +565,8 @@ namespace ClassFilter /** * Creates a blueprint from a class. * - * @param InOutClassNode Class node to pull what class to load and to update information in. + * @param InOutClassNode Class node to pull what class to load and to update information + * in. */ static void LoadClass(FSEClassFilterNodePtr InOutClassNode) { @@ -538,28 +579,31 @@ namespace ClassFilter InOutClassNode->Blueprint = Cast(Class->ClassGeneratedBy); InOutClassNode->Class = Class; - // Tell the original node to update so when a refresh happens it will still know about the newly loaded class. - ClassFilter::Helpers::UpdateClassInNode(InOutClassNode->ClassPath, InOutClassNode->Class.Get(), InOutClassNode->Blueprint.Get() ); + // Tell the original node to update so when a refresh happens it will still know about the + // newly loaded class. + ClassFilter::Helpers::UpdateClassInNode( + InOutClassNode->ClassPath, InOutClassNode->Class.Get(), InOutClassNode->Blueprint.Get()); } else { FMessageLog EditorErrors("EditorErrors"); FFormatNamedArguments Arguments; Arguments.Add(TEXT("ObjectName"), FText::FromString(InOutClassNode->ClassPath.ToString())); - EditorErrors.Error(FText::Format(LOCTEXT("PackageLoadFail", "Failed to load class {ObjectName}"), Arguments)); + EditorErrors.Error(FText::Format( + LOCTEXT("PackageLoadFail", "Failed to load class {ObjectName}"), Arguments)); } } /** Updates the Class of a node. Uses the generated class package name to find the node. - * @param InGeneratedClassPath The name of the generated class to find the node for. - * @param InNewClass The class to update the node with. - */ + * @param InGeneratedClassPath The name of the generated class to find the node for. + * @param InNewClass The class to update the node with. + */ static void UpdateClassInNode( FTopLevelAssetPath InGeneratedClassPath, UClass* InNewClass, UBlueprint* InNewBluePrint) { - ClassHierarchy->UpdateClassInNode(InGeneratedClassPath, InNewClass, InNewBluePrint ); + ClassHierarchy->UpdateClassInNode(InGeneratedClassPath, InNewClass, InNewBluePrint); } - } // namespace Helpers -} // namespace ClassFilter + } // namespace Helpers +} // namespace ClassFilter #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp index d0e0a10..9d72998 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp @@ -1,10 +1,12 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "ClassFilterNode.h" + +#include "Misc/ClassFilter.h" + #include #include -#include "Misc/ClassFilter.h" FSEClassFilterNode::FSEClassFilterNode(const FString& InClassName, const FString& InClassDisplayName) @@ -18,7 +20,7 @@ FSEClassFilterNode::FSEClassFilterNode(const FString& InClassName, const FString Blueprint = nullptr; } -FSEClassFilterNode::FSEClassFilterNode( const FSEClassFilterNode& InCopyObject) +FSEClassFilterNode::FSEClassFilterNode(const FSEClassFilterNode& InCopyObject) { ClassName = InCopyObject.ClassName; ClassDisplayName = InCopyObject.ClassDisplayName; @@ -47,7 +49,7 @@ FSEClassFilterNode::FSEClassFilterNode( const FSEClassFilterNode& InCopyObject) void FSEClassFilterNode::AddChild(FSEClassFilterNodePtr& Child) { ChildrenList.Add(Child); - Child->ParentNode = TSharedRef{ AsShared() }; + Child->ParentNode = TSharedRef{AsShared()}; } void FSEClassFilterNode::AddUniqueChild(FSEClassFilterNodePtr& Child) @@ -65,7 +67,8 @@ void FSEClassFilterNode::AddUniqueChild(FSEClassFilterNodePtr& Child) if (bNewChildHasMoreInfo && !bOldChildHasMoreInfo) { // make sure, that new child has all needed children - for (int OldChildIndex = 0; OldChildIndex < CurrentChild->ChildrenList.Num(); ++OldChildIndex) + for (int OldChildIndex = 0; OldChildIndex < CurrentChild->ChildrenList.Num(); + ++OldChildIndex) { Child->AddUniqueChild(CurrentChild->ChildrenList[OldChildIndex]); } @@ -85,27 +88,28 @@ FString FSEClassFilterNode::GetClassName(EClassViewerNameTypeToDisplay NameType) { switch (NameType) { - case EClassViewerNameTypeToDisplay::ClassName: - return ClassName; + case EClassViewerNameTypeToDisplay::ClassName: + return ClassName; - case EClassViewerNameTypeToDisplay::DisplayName: - return ClassDisplayName; + case EClassViewerNameTypeToDisplay::DisplayName: + return ClassDisplayName; - case EClassViewerNameTypeToDisplay::Dynamic: - FString CombinedName; - FString SanitizedName = FName::NameToDisplayString(ClassName, false); - if (ClassDisplayName.IsEmpty() && !ClassDisplayName.Equals(SanitizedName) && !ClassDisplayName.Equals(ClassName)) - { - TArray Args; - Args.Add(ClassName); - Args.Add(ClassDisplayName); - CombinedName = FString::Format(TEXT("{0} ({1})"), Args); - } - else - { - CombinedName = ClassName; - } - return MoveTemp(CombinedName); + case EClassViewerNameTypeToDisplay::Dynamic: + FString CombinedName; + FString SanitizedName = FName::NameToDisplayString(ClassName, false); + if (ClassDisplayName.IsEmpty() && !ClassDisplayName.Equals(SanitizedName) && + !ClassDisplayName.Equals(ClassName)) + { + TArray Args; + Args.Add(ClassName); + Args.Add(ClassDisplayName); + CombinedName = FString::Format(TEXT("{0} ({1})"), Args); + } + else + { + CombinedName = ClassName; + } + return MoveTemp(CombinedName); } ensureMsgf(false, TEXT("FSEClassFilterNode::GetClassName called with invalid name type.")); @@ -139,7 +143,7 @@ void FSEClassFilterNode::SetOwnFilterState(EClassFilterState State) void FSEClassFilterNode::SetStateFromFilter(const FSEClassFilter& Filter) { - const TSoftClassPtr<> ClassAsset{ ClassPath.ToString() }; + const TSoftClassPtr<> ClassAsset{ClassPath.ToString()}; if (Filter.AllowedClasses.Contains(ClassAsset)) { diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.h b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.h index 039769d..5880141 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.h +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.h @@ -1,10 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include #include -#include +#include #include +#include + class IPropertyHandle; class IUnloadedBlueprintData; @@ -22,7 +23,6 @@ enum class EClassFilterState : uint8 class FSEClassFilterNode : public TSharedFromThis { public: - using FPtr = TSharedPtr; /** @@ -32,9 +32,9 @@ class FSEClassFilterNode : public TSharedFromThis * @param InClassDisplayName The display name of the class this node represents * @param bInIsPlaceable true if the class is a placeable class. */ - FSEClassFilterNode( const FString& InClassName, const FString& InClassDisplayName ); + FSEClassFilterNode(const FString& InClassName, const FString& InClassDisplayName); - FSEClassFilterNode( const FSEClassFilterNode& InCopyObject); + FSEClassFilterNode(const FSEClassFilterNode& InCopyObject); /** * Adds the specified child to the node. @@ -45,7 +45,8 @@ class FSEClassFilterNode : public TSharedFromThis void AddUniqueChild(FPtr& Child); /** - * Retrieves the class name this node is associated with. This is not the literal UClass name as it is missing the _C for blueprints + * Retrieves the class name this node is associated with. This is not the literal UClass name as it is + * missing the _C for blueprints * @param bUseDisplayName Whether to use the display name or class name */ const FString& GetClassName(bool bUseDisplayName = false) const @@ -54,7 +55,8 @@ class FSEClassFilterNode : public TSharedFromThis } /** - * Retrieves the class name this node is associated with. This is not the literal UClass name as it is missing the _C for blueprints + * Retrieves the class name this node is associated with. This is not the literal UClass name as it is + * missing the _C for blueprints * @param NameType Whether to use the display name or class name */ FString GetClassName(EClassViewerNameTypeToDisplay NameType) const; @@ -74,12 +76,15 @@ class FSEClassFilterNode : public TSharedFromThis /** Filter states */ void SetOwnFilterState(EClassFilterState State); void SetStateFromFilter(const struct FSEClassFilter& Filter); - EClassFilterState GetOwnFilterState() const { return FilterState; } + EClassFilterState GetOwnFilterState() const + { + return FilterState; + } EClassFilterState GetParentFilterState() const; private: - - /** The non-translated internal name for this class. This is not necessarily the UClass's name, as that may have _C for blueprints */ + /** The non-translated internal name for this class. This is not necessarily the UClass's name, as that + * may have _C for blueprints */ FString ClassName; /** The translated display name for this class */ @@ -91,7 +96,6 @@ class FSEClassFilterNode : public TSharedFromThis EClassFilterState FilterState = EClassFilterState::None; public: - TWeakPtr ParentNode; /** The class this node is associated with. */ @@ -112,7 +116,8 @@ class FSEClassFilterNode : public TSharedFromThis /** true if the class passed the filter. */ bool bPassesFilter; - /** true if the class is a "normal type", this is used to identify unloaded blueprints as blueprint bases. */ + /** true if the class is a "normal type", this is used to identify unloaded blueprints as blueprint bases. + */ bool bIsBPNormalType; /** Data for unloaded blueprints, only valid if the class is unloaded. */ diff --git a/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.cpp b/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.cpp index 68f0dd6..156932e 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.cpp +++ b/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.cpp @@ -1,40 +1,43 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SClassFilter.h" + +#include "AssetRegistry/AssetData.h" +#include "ClassFilterHelpers.h" +#include "Dialogs/Dialogs.h" +#include "Editor.h" +#include "EditorStyleSet.h" +#include "Framework/Application/SlateApplication.h" +#include "Framework/Commands/UIAction.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Framework/Notifications/NotificationManager.h" +#include "GameplayTagsModule.h" +#include "GameplayTagsSettings.h" +#include "Layout/WidgetPath.h" #include "Misc/ConfigCacheIni.h" +#include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SComboButton.h" -#include "Widgets/Images/SImage.h" -#include "EditorStyleSet.h" #include "Widgets/SWindow.h" -#include "Dialogs/Dialogs.h" -#include "GameplayTagsModule.h" + +#include +#include +#include #include #include -#include -#include #include #include -#include #include +#include #include -#include "Framework/Notifications/NotificationManager.h" -#include "GameplayTagsSettings.h" -#include "Layout/WidgetPath.h" -#include "Framework/Application/SlateApplication.h" -#include "AssetRegistry/AssetData.h" -#include "Editor.h" -#include "Framework/Commands/UIAction.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "ClassFilterHelpers.h" -#include #define LOCTEXT_NAMESPACE "GameplayTagWidget" -void SClassFilter::Construct(const FArguments& InArgs, const TArray& EditableFilters) +void SClassFilter::Construct( + const FArguments& InArgs, const TArray& EditableFilters) { bNeedsRefresh = true; @@ -74,76 +77,53 @@ void SClassFilter::Construct(const FArguments& InArgs, const TArray) - .TreeItemsSource(&RootClasses) - .OnGenerateRow(this, &SClassFilter::OnGenerateRow) - .OnGetChildren(this, &SClassFilter::OnGetChildren) - .OnExpansionChanged( this, &SClassFilter::OnExpansionChanged) - .SelectionMode(ESelectionMode::Multi) - ] - ] - ] - ]; + [SNew(SBorder).BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + [SNew(SVerticalBox) + + // Gameplay Tag Tree controls + + + SVerticalBox::Slot().AutoHeight().VAlign(VAlign_Top) + [SNew(SHorizontalBox) + + // Search + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .FillWidth(1.f) + .Padding(5, 1, 5, + 1)[SAssignNew(SearchBox, SSearchBox) + .HintText(LOCTEXT("ClassFilter_SearchBoxHint", "Search Classes")) + .OnTextChanged(this, &SClassFilter::OnSearchTextChanged)] + + // Expand All nodes + + SHorizontalBox::Slot() + .AutoWidth()[SNew(SButton) + .OnClicked(this, &SClassFilter::OnClickedExpandAll) + .Text(LOCTEXT("ClassFilter_ExpandAll", "Expand All"))] + + // Collapse All nodes + + SHorizontalBox::Slot() + .AutoWidth()[SNew(SButton) + .OnClicked(this, &SClassFilter::OnClickedCollapseAll) + .Text(LOCTEXT("ClassFilter_CollapseAll", "Collapse All"))] + + // Clear selections + + SHorizontalBox::Slot() + .AutoWidth()[SNew(SButton) + .OnClicked(this, &SClassFilter::OnClickedClearAll) + .Text(LOCTEXT("ClassFilter_ClearAll", "Clear All")) + .Visibility(this, + &SClassFilter::DetermineClearSelectionVisibility)]] + + // Classes tree + + SVerticalBox::Slot().MaxHeight(MaxHeight) + [SAssignNew(TreeContainerWidget, SBorder) + .Padding(FMargin( + 4.f))[SAssignNew(TreeWidget, STreeView) + .TreeItemsSource(&RootClasses) + .OnGenerateRow(this, &SClassFilter::OnGenerateRow) + .OnGetChildren(this, &SClassFilter::OnGetChildren) + .OnExpansionChanged(this, &SClassFilter::OnExpansionChanged) + .SelectionMode(ESelectionMode::Multi)]]]]; // Construct the class hierarchy. ClassFilter::Helpers::ConstructClassHierarchy(); @@ -155,7 +135,7 @@ void SClassFilter::Construct(const FArguments& InArgs, const TArrayGetDesiredSize(); @@ -168,7 +148,7 @@ FVector2D SClassFilter::ComputeDesiredSize(float LayoutScaleMultiplier) const return WidgetSize; } -void SClassFilter::OnSearchTextChanged( const FText& SearchText ) +void SClassFilter::OnSearchTextChanged(const FText& SearchText) { SearchString = SearchText.ToString(); @@ -230,53 +210,39 @@ bool SClassFilter::FilterChildrenCheck(const FSEClassFilterNodePtr& Class) return false; } -TSharedRef SClassFilter::OnGenerateRow(FSEClassFilterNodePtr Class, const TSharedRef& OwnerTable) +TSharedRef SClassFilter::OnGenerateRow( + FSEClassFilterNodePtr Class, const TSharedRef& OwnerTable) { return SNew(STableRow, OwnerTable) - .Style(FAppStyle::Get(), "GameplayTagTreeView") - [ - SNew(SBorder) - .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) - .BorderBackgroundColor(this, &SClassFilter::GetClassBackgroundColor, Class) - .Padding(0) - .Content() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .HAlign(HAlign_Left) - [ - SNew(SButton) - .ButtonStyle(FAppStyle::Get(), "FlatButton") - .OnClicked(this, &SClassFilter::OnClassClicked, Class) - .ForegroundColor(this, &SClassFilter::GetClassIconColor, Class) - .ContentPadding(0) - .IsEnabled(this, &SClassFilter::CanSelectClasses) - [ - SNew(STextBlock) - .Font(FAppStyle::Get().GetFontStyle("FontAwesome.8")) - .Text(this, &SClassFilter::GetClassIconText, Class) - ] - ] - // Tag Selection (selection mode only) - +SHorizontalBox::Slot() - .FillWidth(1.0f) - .HAlign(HAlign_Left) - [ - SNew(STextBlock) - .Text(FText::FromString(Class->GetClassName(true))) - .ToolTipText(Class->GetClassTooltip()) - .IsEnabled(this, &SClassFilter::CanSelectClasses) - ] - ] - ]; + .Style(FAppStyle::Get(), "GameplayTagTreeView") + [SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .BorderBackgroundColor(this, &SClassFilter::GetClassBackgroundColor, Class) + .Padding(0) + .Content()[SNew(SHorizontalBox) + + SHorizontalBox::Slot().AutoWidth().HAlign(HAlign_Left) + [SNew(SButton) + .ButtonStyle(FAppStyle::Get(), "FlatButton") + .OnClicked(this, &SClassFilter::OnClassClicked, Class) + .ForegroundColor(this, &SClassFilter::GetClassIconColor, Class) + .ContentPadding(0) + .IsEnabled(this, &SClassFilter::CanSelectClasses) + [SNew(STextBlock) + .Font(FAppStyle::Get().GetFontStyle("FontAwesome.8")) + .Text(this, &SClassFilter::GetClassIconText, Class)]] + // Tag Selection (selection mode only) + + SHorizontalBox::Slot().FillWidth(1.0f).HAlign( + HAlign_Left)[SNew(STextBlock) + .Text(FText::FromString(Class->GetClassName(true))) + .ToolTipText(Class->GetClassTooltip()) + .IsEnabled(this, &SClassFilter::CanSelectClasses)]]]; } void SClassFilter::OnGetChildren(FSEClassFilterNodePtr Class, TArray& OutChildren) { - for(auto& ChildClass : Class->GetChildrenList()) + for (auto& ChildClass : Class->GetChildrenList()) { - if(FilterChildrenCheck(ChildClass)) + if (FilterChildrenCheck(ChildClass)) { OutChildren.Add(ChildClass); } @@ -309,11 +275,11 @@ FText SClassFilter::GetClassIconText(FSEClassFilterNodePtr Class) const { switch (Class->GetOwnFilterState()) { - case EClassFilterState::Allowed: - return FText::FromString(FString(TEXT("\xf00c"))) /*fa-check*/; + case EClassFilterState::Allowed: + return FText::FromString(FString(TEXT("\xf00c"))) /*fa-check*/; - case EClassFilterState::Denied: - return FText::FromString(FString(TEXT("\xf00d"))) /*fa-times*/; + case EClassFilterState::Denied: + return FText::FromString(FString(TEXT("\xf00d"))) /*fa-times*/; } return FText::FromString(FString(TEXT("\xf096"))) /*fa-square-o*/; @@ -323,11 +289,11 @@ FSlateColor SClassFilter::GetClassIconColor(FSEClassFilterNodePtr Class) const { switch (Class->GetOwnFilterState()) { - case EClassFilterState::Allowed: - return { FLinearColor::Green }; + case EClassFilterState::Allowed: + return {FLinearColor::Green}; - case EClassFilterState::Denied: - return { FLinearColor::Red }; + case EClassFilterState::Denied: + return {FLinearColor::Red}; } return FSlateColor::UseForeground(); @@ -335,69 +301,71 @@ FSlateColor SClassFilter::GetClassIconColor(FSEClassFilterNodePtr Class) const void SClassFilter::MarkClass(FSEClassFilterNodePtr Class, EClassFilterState State) { - const TSoftClassPtr<> ClassAsset{ Class->ClassPath.ToString() }; + const TSoftClassPtr<> ClassAsset{Class->ClassPath.ToString()}; switch (State) { - case EClassFilterState::Allowed: - { - FScopedTransaction Transaction(LOCTEXT("ClassFilter_AllowClass", "Allow Class")); - if (Class) - { - Class->SetOwnFilterState(State); - - if(PropertyHandle) PropertyHandle->NotifyPreChange(); - for (const auto& Filter : Filters) + case EClassFilterState::Allowed: { + FScopedTransaction Transaction(LOCTEXT("ClassFilter_AllowClass", "Allow Class")); + if (Class) { - Filter.Filter->AllowedClasses.Add(ClassAsset); - Filter.Filter->IgnoredClasses.Remove(ClassAsset); + Class->SetOwnFilterState(State); + + if (PropertyHandle) + PropertyHandle->NotifyPreChange(); + for (const auto& Filter : Filters) + { + Filter.Filter->AllowedClasses.Add(ClassAsset); + Filter.Filter->IgnoredClasses.Remove(ClassAsset); + } + if (PropertyHandle) + PropertyHandle->NotifyPostChange(EPropertyChangeType::Unspecified); } - if (PropertyHandle) PropertyHandle->NotifyPostChange(EPropertyChangeType::Unspecified); + break; } - break; - } - case EClassFilterState::Denied: - { - FScopedTransaction Transaction(LOCTEXT("ClassFilter_DeniedClass", "Deny Class")); - if (Class) - { - Class->SetOwnFilterState(State); - - if (PropertyHandle) PropertyHandle->NotifyPreChange(); - for (const auto& Filter : Filters) + case EClassFilterState::Denied: { + FScopedTransaction Transaction(LOCTEXT("ClassFilter_DeniedClass", "Deny Class")); + if (Class) { - Filter.Filter->IgnoredClasses.Add(ClassAsset); - Filter.Filter->AllowedClasses.Remove(ClassAsset); + Class->SetOwnFilterState(State); + + if (PropertyHandle) + PropertyHandle->NotifyPreChange(); + for (const auto& Filter : Filters) + { + Filter.Filter->IgnoredClasses.Add(ClassAsset); + Filter.Filter->AllowedClasses.Remove(ClassAsset); + } + if (PropertyHandle) + PropertyHandle->NotifyPostChange(EPropertyChangeType::Unspecified); } - if (PropertyHandle) - PropertyHandle->NotifyPostChange(EPropertyChangeType::Unspecified); + break; } - break; - } - case EClassFilterState::None: - { - FScopedTransaction Transaction(LOCTEXT("ClassFilter_UnmarkClass", "Unmark Class")); - if (Class) - { - Class->SetOwnFilterState(State); - - if (PropertyHandle) PropertyHandle->NotifyPreChange(); - for (const auto& Filter : Filters) + case EClassFilterState::None: { + FScopedTransaction Transaction(LOCTEXT("ClassFilter_UnmarkClass", "Unmark Class")); + if (Class) { - Filter.Filter->IgnoredClasses.Remove(ClassAsset); - Filter.Filter->AllowedClasses.Remove(ClassAsset); + Class->SetOwnFilterState(State); + + if (PropertyHandle) + PropertyHandle->NotifyPreChange(); + for (const auto& Filter : Filters) + { + Filter.Filter->IgnoredClasses.Remove(ClassAsset); + Filter.Filter->AllowedClasses.Remove(ClassAsset); + } + if (PropertyHandle) + PropertyHandle->NotifyPostChange(EPropertyChangeType::Unspecified); } - if (PropertyHandle) - PropertyHandle->NotifyPostChange(EPropertyChangeType::Unspecified); + break; } - break; - }} + } OnFilterChanged.ExecuteIfBound(); } FReply SClassFilter::OnClickedClearAll() { - FScopedTransaction Transaction( LOCTEXT("GameplayTagWidget_RemoveAllTags", "Remove All Gameplay Tags") ); + FScopedTransaction Transaction(LOCTEXT("GameplayTagWidget_RemoveAllTags", "Remove All Gameplay Tags")); for (int32 ContainerIdx = 0; ContainerIdx < Filters.Num(); ++ContainerIdx) { @@ -426,7 +394,7 @@ FSlateColor SClassFilter::GetClassBackgroundColor(FSEClassFilterNodePtr Class) c { Color = FLinearColor::Red; } - else if(ParentState == EClassFilterState::Allowed) + else if (ParentState == EClassFilterState::Allowed) { Color = FLinearColor::Green; } @@ -436,10 +404,10 @@ FSlateColor SClassFilter::GetClassBackgroundColor(FSEClassFilterNodePtr Class) c } else { - return { FLinearColor::Transparent }; + return {FLinearColor::Transparent}; } Color.A = 0.3f; - return { Color }; + return {Color}; } FReply SClassFilter::OnClickedExpandAll() @@ -499,19 +467,20 @@ void SClassFilter::SetFilter(FSEClassFilter* OriginalFilter, FSEClassFilter* Edi if (PropertyHandle.IsValid() && bMultiSelect) { // Case for a tag container - //PropertyHandle->SetValueFromFormattedString(EditedFilter->ToString()); + // PropertyHandle->SetValueFromFormattedString(EditedFilter->ToString()); } else if (PropertyHandle.IsValid() && !bMultiSelect) { // Case for a single Tag - //FString FormattedString = TEXT("(TagName=\""); - //FormattedString += EditedFilter.First().GetTagName().ToString(); - //FormattedString += TEXT("\")"); - //PropertyHandle->SetValueFromFormattedString(FormattedString); + // FString FormattedString = TEXT("(TagName=\""); + // FormattedString += EditedFilter.First().GetTagName().ToString(); + // FormattedString += TEXT("\")"); + // PropertyHandle->SetValueFromFormattedString(FormattedString); } else { - // Not sure if we should get here, means the property handle hasn't been setup which could be right or wrong. + // Not sure if we should get here, means the property handle hasn't been setup which could be right or + // wrong. if (OwnerObj) { OwnerObj->PreEditChange(PropertyHandle.IsValid() ? PropertyHandle->GetProperty() : nullptr); @@ -536,7 +505,8 @@ void SClassFilter::Refresh() bNeedsRefresh = true; } -void SClassFilter::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +void SClassFilter::Tick( + const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { // Will populate the class hierarchy if needed ClassFilter::Helpers::PopulateClassHierarchy(); @@ -570,7 +540,8 @@ void SClassFilter::Populate() TArray InternalClasses; TArray InternalPaths; - // We aren't showing the internal classes, then we need to know what classes to consider Internal Only, so let's gather them up from the settings object. + // We aren't showing the internal classes, then we need to know what classes to consider Internal Only, so + // let's gather them up from the settings object. GetInternalOnlyPaths(InternalPaths); GetInternalOnlyClasses(InternalClassNames); @@ -579,8 +550,7 @@ void SClassFilter::Populate() { FString PackageClassName = InternalClassNames[i].ToString(); const FSEClassFilterNodePtr ClassNode = ClassFilter::Helpers::ClassHierarchy->FindNodeByClassName( - ClassFilter::Helpers::ClassHierarchy->GetObjectRootNode(), PackageClassName - ); + ClassFilter::Helpers::ClassHierarchy->GetObjectRootNode(), PackageClassName); if (ClassNode.IsValid()) { @@ -593,7 +563,8 @@ void SClassFilter::Populate() FSEClassFilterNodePtr RootNode; // Get the class tree, passing in certain filter options. - ClassFilter::Helpers::GetClassTree(*Filters[0].Filter, RootNode, false, true, false, InternalClasses, InternalPaths); + ClassFilter::Helpers::GetClassTree( + *Filters[0].Filter, RootNode, false, true, false, InternalClasses, InternalPaths); // Add all the children of the "Object" root. for (const auto& Child : RootNode->GetChildrenList()) @@ -620,10 +591,7 @@ EVisibility SClassFilter::DetermineClearSelectionVisibility() const return CanSelectClasses() ? EVisibility::Visible : EVisibility::Collapsed; } -void SClassFilter::OnExpansionChanged(FSEClassFilterNodePtr Class, bool bIsExpanded) -{ - -} +void SClassFilter::OnExpansionChanged(FSEClassFilterNodePtr Class, bool bIsExpanded) {} bool SClassFilter::CanSelectClasses() const { diff --git a/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h b/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h index bd2aca7..bb566cb 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h +++ b/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h @@ -1,21 +1,22 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "ClassFilterNode.h" #include "CoreMinimal.h" -#include +#include "Input/Reply.h" +#include "Layout/Visibility.h" +#include "Misc/ClassFilter.h" #include "SlateFwd.h" #include "UObject/Object.h" -#include "Layout/Visibility.h" #include "Widgets/DeclarativeSyntaxSupport.h" -#include "Input/Reply.h" -#include "Widgets/SWidget.h" #include "Widgets/SCompoundWidget.h" -#include "Widgets/Views/STableViewBase.h" +#include "Widgets/SWidget.h" #include "Widgets/Views/STableRow.h" +#include "Widgets/Views/STableViewBase.h" #include "Widgets/Views/STreeView.h" -#include "Misc/ClassFilter.h" -#include "ClassFilterNode.h" +#include + class IPropertyHandle; @@ -23,7 +24,6 @@ class IPropertyHandle; class SClassFilter : public SCompoundWidget { public: - /** Called when the filter is changed */ DECLARE_DELEGATE(FOnFilterChanged) @@ -33,15 +33,15 @@ class SClassFilter : public SCompoundWidget , _PropertyHandle(nullptr) , _MaxHeight(260.0f) {} - SLATE_ARGUMENT(bool, ReadOnly) // Flag to set if the list is read only - SLATE_ARGUMENT(bool, MultiSelect) // If we can select multiple entries - SLATE_ARGUMENT(TSharedPtr, PropertyHandle) - SLATE_EVENT(FOnFilterChanged, OnFilterChanged) // Called when a tag status changes - SLATE_ARGUMENT(float, MaxHeight) // caps the height of the gameplay tag tree - SLATE_END_ARGS() - - /** Simple struct holding a tag container and its owner for generic re-use of the widget */ - struct FEditableClassFilterDatum + SLATE_ARGUMENT(bool, ReadOnly) // Flag to set if the list is read only + SLATE_ARGUMENT(bool, MultiSelect) // If we can select multiple entries + SLATE_ARGUMENT(TSharedPtr, PropertyHandle) + SLATE_EVENT(FOnFilterChanged, OnFilterChanged) // Called when a tag status changes + SLATE_ARGUMENT(float, MaxHeight) // caps the height of the gameplay tag tree + SLATE_END_ARGS() + + /** Simple struct holding a tag container and its owner for generic re-use of the widget */ + struct FEditableClassFilterDatum { /** Constructor */ FEditableClassFilterDatum(class UObject* InOwner, struct FSEClassFilter* InFilter) @@ -59,7 +59,8 @@ class SClassFilter : public SCompoundWidget /** Construct the actual widget */ void Construct(const FArguments& InArgs, const TArray& EditableFilters); - virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; + virtual void Tick( + const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; /** Ensures that this widget will always account for the MaxHeight if it's specified */ virtual FVector2D ComputeDesiredSize(float LayoutScaleMultiplier) const override; @@ -75,7 +76,6 @@ class SClassFilter : public SCompoundWidget private: - FString SearchString; bool bReadOnly; @@ -86,7 +86,8 @@ class SClassFilter : public SCompoundWidget /** The maximum height of the gameplay tag tree. If 0, the height is unbound. */ float MaxHeight; - /** True if the Class Filter needs to be repopulated at the next appropriate opportunity, occurs whenever classes are added, removed, renamed, etc. */ + /** True if the Class Filter needs to be repopulated at the next appropriate opportunity, occurs whenever + * classes are added, removed, renamed, etc. */ bool bNeedsRefresh; /* Array of tags to be displayed in the TreeView */ @@ -121,7 +122,8 @@ class SClassFilter : public SCompoundWidget * * @return Generated row widget for the item node */ - TSharedRef OnGenerateRow(FSEClassFilterNodePtr Class, const TSharedRef& OwnerTable); + TSharedRef OnGenerateRow( + FSEClassFilterNodePtr Class, const TSharedRef& OwnerTable); /** * Get children nodes of the specified node diff --git a/Source/Editor/Private/Customizations/SClassFilterGraphPin.cpp b/Source/Editor/Private/Customizations/SClassFilterGraphPin.cpp index 347bffe..930e59a 100644 --- a/Source/Editor/Private/Customizations/SClassFilterGraphPin.cpp +++ b/Source/Editor/Private/Customizations/SClassFilterGraphPin.cpp @@ -1,40 +1,34 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SClassFilterGraphPin.h" -#include "Widgets/Input/SComboButton.h" + #include "GameplayTagsModule.h" +#include "Widgets/Input/SComboButton.h" #include "Widgets/Layout/SScaleBox.h" + #define LOCTEXT_NAMESPACE "GameplayTagGraphPin" void SClassFilterGraphPin::Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj) { - SGraphPin::Construct( SGraphPin::FArguments(), InGraphPinObj ); + SGraphPin::Construct(SGraphPin::FArguments(), InGraphPinObj); } -TSharedRef SClassFilterGraphPin::GetDefaultValueWidget() +TSharedRef SClassFilterGraphPin::GetDefaultValueWidget() { ParseDefaultValueData(); - //Create widget - return SNew(SHorizontalBox) - +SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Fill) - [ - SAssignNew( ComboButton, SComboButton ) - .OnGetMenuContent(this, &SClassFilterGraphPin::GetListContent) - .ButtonStyle(FAppStyle::Get(), "FlatButton") - .ForegroundColor(FSlateColor::UseForeground()) - .ContentPadding(FMargin(0.0f, 2.0f)) - .MenuPlacement(MenuPlacement_BelowAnchor) - .Visibility( this, &SGraphPin::GetDefaultValueVisibility ) - ] - +SHorizontalBox::Slot() - .AutoWidth() - [ - SelectedTags() - ]; + // Create widget + return SNew(SHorizontalBox) + + SHorizontalBox::Slot().AutoWidth().VAlign( + VAlign_Fill)[SAssignNew(ComboButton, SComboButton) + .OnGetMenuContent(this, &SClassFilterGraphPin::GetListContent) + .ButtonStyle(FAppStyle::Get(), "FlatButton") + .ForegroundColor(FSlateColor::UseForeground()) + .ContentPadding(FMargin(0.0f, 2.0f)) + .MenuPlacement(MenuPlacement_BelowAnchor) + .Visibility(this, &SGraphPin::GetDefaultValueVisibility)] + + SHorizontalBox::Slot().AutoWidth()[SelectedTags()]; } void SClassFilterGraphPin::ParseDefaultValueData() @@ -45,24 +39,19 @@ void SClassFilterGraphPin::ParseDefaultValueData() TSharedRef SClassFilterGraphPin::GetListContent() { EditableFilters.Empty(); - EditableFilters.Add( SClassFilter::FEditableClassFilterDatum( GraphPinObj->GetOwningNode(), &Filter ) ); - - return SNew( SVerticalBox ) - +SVerticalBox::Slot() - .AutoHeight() - .MaxHeight( 400 ) - [ - SNew( SClassFilter, EditableFilters ) - .OnFilterChanged(this, &SClassFilterGraphPin::RefreshPreviewList) - .Visibility( this, &SGraphPin::GetDefaultValueVisibility ) - ]; + EditableFilters.Add(SClassFilter::FEditableClassFilterDatum(GraphPinObj->GetOwningNode(), &Filter)); + + return SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight().MaxHeight( + 400)[SNew(SClassFilter, EditableFilters) + .OnFilterChanged(this, &SClassFilterGraphPin::RefreshPreviewList) + .Visibility(this, &SGraphPin::GetDefaultValueVisibility)]; } TSharedRef SClassFilterGraphPin::SelectedTags() { RefreshPreviewList(); - SAssignNew( PreviewList, SListView> ) + SAssignNew(PreviewList, SListView>) .ListItemsSource(&PreviewClasses) .SelectionMode(ESelectionMode::None) .OnGenerateRow(this, &SClassFilterGraphPin::OnGeneratePreviewRow); @@ -70,7 +59,8 @@ TSharedRef SClassFilterGraphPin::SelectedTags() return PreviewList->AsShared(); } -TSharedRef SClassFilterGraphPin::OnGeneratePreviewRow(TSharedPtr Class, const TSharedRef& OwnerTable) +TSharedRef SClassFilterGraphPin::OnGeneratePreviewRow( + TSharedPtr Class, const TSharedRef& OwnerTable) { FLinearColor StateColor; FText StateText; @@ -85,26 +75,17 @@ TSharedRef SClassFilterGraphPin::OnGeneratePreviewRow(TSharedPtr>, OwnerTable) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .Padding(0, 0, 2, 0) - [ - SNew(STextBlock) - .ColorAndOpacity(StateColor) - .Font(FAppStyle::Get().GetFontStyle("FontAwesome.8")) - .Text(StateText) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - [ - SNew(STextBlock).Text(FText::FromString(Class->ClassName)) - ] - ]; + return SNew(STableRow>, + OwnerTable)[SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(0, 0, 2, 0)[SNew(STextBlock) + .ColorAndOpacity(StateColor) + .Font(FAppStyle::Get().GetFontStyle("FontAwesome.8")) + .Text(StateText)] + + SHorizontalBox::Slot().AutoWidth().VAlign( + VAlign_Center)[SNew(STextBlock).Text(FText::FromString(Class->ClassName))]]; } void SClassFilterGraphPin::RefreshPreviewList() diff --git a/Source/Editor/Private/Customizations/SClassFilterGraphPin.h b/Source/Editor/Private/Customizations/SClassFilterGraphPin.h index 170532a..3d95914 100644 --- a/Source/Editor/Private/Customizations/SClassFilterGraphPin.h +++ b/Source/Editor/Private/Customizations/SClassFilterGraphPin.h @@ -1,14 +1,16 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "ClassFilter/SClassFilter.h" +#include "SEClassFilterCustomization.h" +#include "SGraphPin.h" + #include #include #include -#include #include -#include "ClassFilter/SClassFilter.h" -#include "SGraphPin.h" -#include "SEClassFilterCustomization.h" +#include + class SComboButton; @@ -22,11 +24,10 @@ class SClassFilterGraphPin : public SGraphPin void Construct(const FArguments& InArgs, UEdGraphPin* InGraphPinObj); //~ Begin SGraphPin Interface - virtual TSharedRef GetDefaultValueWidget() override; + virtual TSharedRef GetDefaultValueWidget() override; //~ End SGraphPin Interface private: - /** Refreshes the list of tags displayed on the node. */ void RefreshPreviewList(); @@ -46,10 +47,10 @@ class SClassFilterGraphPin : public SGraphPin * Callback for populating rows of the SelectedTags List View. * @return widget that contains the name of a tag. */ - TSharedRef OnGeneratePreviewRow(TSharedPtr Class, const TSharedRef& OwnerTable); + TSharedRef OnGeneratePreviewRow( + TSharedPtr Class, const TSharedRef& OwnerTable); private: - // Combo Button for the drop down list. TSharedPtr ComboButton; diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp index fdcafed..9ee5f38 100644 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Customizations/SEActorClassFilterCustomization.h" @@ -8,9 +8,11 @@ #define LOCTEXT_NAMESPACE "FSEActorClassFilterCustomization" -TSharedPtr FSEActorClassFilterCustomization::GetFilterHandle(TSharedRef StructPropertyHandle) +TSharedPtr FSEActorClassFilterCustomization::GetFilterHandle( + TSharedRef StructPropertyHandle) { - return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEActorClassFilter, ClassFilter));; + return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEActorClassFilter, ClassFilter)); + ; } #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h index 069cb9f..9078c45 100644 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h +++ b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "SEClassFilterCustomization.h" @@ -9,19 +9,17 @@ class IPropertyHandle; class FSEActorClassFilterCustomization : public FSEClassFilterCustomization { public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ + * Creates a new instance. + * + * @return A new struct customization for Factions. + */ static TSharedRef MakeInstance() { return MakeShared(); } protected: - - virtual TSharedPtr GetFilterHandle(TSharedRef StructPropertyHandle) override; + virtual TSharedPtr GetFilterHandle( + TSharedRef StructPropertyHandle) override; }; - diff --git a/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp index 43a275b..4f19d84 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp @@ -1,15 +1,18 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Customizations/SEClassFilterCustomization.h" #include -#include -#include -#include #include +#include +#include +#include #include +#include +#include #include + #define LOCTEXT_NAMESPACE "FSEClassFilterCustomization" @@ -18,13 +21,15 @@ FSEClassFilterCustomization::~FSEClassFilterCustomization() GEditor->UnregisterForUndo(this); } -void FSEClassFilterCustomization::CustomizeHeader(TSharedRef StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +void FSEClassFilterCustomization::CustomizeHeader(TSharedRef StructPropertyHandle, + FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) { StructHandle = StructPropertyHandle; FilterHandle = GetFilterHandle(StructPropertyHandle); BuildEditableFilterList(); + // clang-format off HeaderRow .NameContent() [ @@ -59,28 +64,16 @@ void FSEClassFilterCustomization::CustomizeHeader(TSharedRefRegisterForUndo(this); } -void FSEClassFilterCustomization::CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +void FSEClassFilterCustomization::CustomizeChildren(TSharedRef StructPropertyHandle, + class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) {} void FSEClassFilterCustomization::BuildEditableFilterList() @@ -97,7 +90,8 @@ void FSEClassFilterCustomization::BuildEditableFilterList() for (int32 ContainerIdx = 0; ContainerIdx < RawStructData.Num(); ++ContainerIdx) { - EditableFilters.Add(SClassFilter::FEditableClassFilterDatum(FirstOuter, (FSEClassFilter*)RawStructData[ContainerIdx])); + EditableFilters.Add(SClassFilter::FEditableClassFilterDatum( + FirstOuter, (FSEClassFilter*) RawStructData[ContainerIdx])); } } } @@ -111,13 +105,13 @@ TSharedRef FSEClassFilterCustomization::GetListContent() bool bReadOnly = FilterHandle->IsEditConst(); - TSharedRef EditPopup = SNew(SClassFilter, EditableFilters) - .ReadOnly(bReadOnly) - .OnFilterChanged(this, &FSEClassFilterCustomization::RefreshClassList) - .PropertyHandle(FilterHandle); - + // clang-format off + TSharedRef EditPopup = + SNew(SClassFilter, EditableFilters) + .ReadOnly(bReadOnly) + .OnFilterChanged(this, &FSEClassFilterCustomization::RefreshClassList) + .PropertyHandle(FilterHandle); LastFilterPopup = EditPopup; - return SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() @@ -125,6 +119,7 @@ TSharedRef FSEClassFilterCustomization::GetListContent() [ EditPopup ]; + // clang-format on } void FSEClassFilterCustomization::OnPopupStateChanged(bool bIsOpened) @@ -139,22 +134,16 @@ void FSEClassFilterCustomization::OnPopupStateChanged(bool bIsOpened) } } -FReply FSEClassFilterCustomization::OnClearClicked() +void FSEClassFilterCustomization::OnClearClicked() { FScopedTransaction Transaction(LOCTEXT("ClassFilter_Filter", "Clear Filter")); + FilterHandle->NotifyPreChange(); for (auto& Filter : EditableFilters) { - // Reset Filter - if (Filter.Owner.IsValid()) - { - Filter.Owner->Modify(); - } - *Filter.Filter = {}; } - + FilterHandle->NotifyPostChange(EPropertyChangeType::ValueSet); RefreshClassList(); - return FReply::Handled(); } EVisibility FSEClassFilterCustomization::GetClassPreviewVisibility() const @@ -170,6 +159,7 @@ TSharedRef FSEClassFilterCustomization::GetClassPreview() { RefreshClassList(); + // clang-format off return SNew(SBox) .MinDesiredWidth(100.f) [ @@ -178,9 +168,11 @@ TSharedRef FSEClassFilterCustomization::GetClassPreview() .ListItemsSource(&PreviewClasses) .OnGenerateRow(this, &FSEClassFilterCustomization::OnGeneratePreviewRow) ]; + // clang-format on } -TSharedRef FSEClassFilterCustomization::OnGeneratePreviewRow(TSharedPtr Class, const TSharedRef& OwnerTable) +TSharedRef FSEClassFilterCustomization::OnGeneratePreviewRow( + TSharedPtr Class, const TSharedRef& OwnerTable) { FLinearColor StateColor; FText StateText; @@ -195,6 +187,7 @@ TSharedRef FSEClassFilterCustomization::OnGeneratePreviewRow(TSharedP StateText = FText::FromString(FString(TEXT("\xf00d"))) /*fa-times*/; } + // clang-format off return SNew(STableRow>, OwnerTable) [ SNew(SHorizontalBox) @@ -215,6 +208,7 @@ TSharedRef FSEClassFilterCustomization::OnGeneratePreviewRow(TSharedP SNew(STextBlock).Text(FText::FromString(Class->ClassName)) ] ]; + // clang-format on } void FSEClassFilterCustomization::PostUndo(bool bSuccess) diff --git a/Source/Editor/Private/Customizations/SEClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEClassFilterCustomization.h index 0a8bfda..ed46b6c 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterCustomization.h +++ b/Source/Editor/Private/Customizations/SEClassFilterCustomization.h @@ -1,27 +1,26 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include -#include #include "ClassFilter/SClassFilter.h" +#include +#include + class IPropertyHandle; -struct FSEClassFilterItem { +struct FSEClassFilterItem +{ FString ClassName; bool bAllowed; - FSEClassFilterItem(FString ClassName, bool bAllowed) - : ClassName{ClassName}, bAllowed{bAllowed} - {} + FSEClassFilterItem(FString ClassName, bool bAllowed) : ClassName{ClassName}, bAllowed{bAllowed} {} }; class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FEditorUndoClient { protected: - /** Filters this customization edits */ TArray EditableFilters; TArray> PreviewClasses; @@ -36,12 +35,11 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE TWeakPtr LastFilterPopup; public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ + * Creates a new instance. + * + * @return A new struct customization for Factions. + */ static TSharedRef MakeInstance() { return MakeShared(); @@ -50,10 +48,13 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE ~FSEClassFilterCustomization(); protected: - /** IPropertyTypeCustomization interface */ - virtual void CustomizeHeader(TSharedRef StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; - virtual void CustomizeChildren(TSharedRef StructPropertyHandle, class IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeHeader(TSharedRef StructPropertyHandle, + class FDetailWidgetRow& HeaderRow, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef StructPropertyHandle, + class IDetailChildrenBuilder& StructBuilder, + IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; //~ Begin FEditorUndoClient Interface virtual void PostUndo(bool bSuccess) override; @@ -73,16 +74,16 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE void OnPopupStateChanged(bool bIsOpened); - FReply OnClearClicked(); + void OnClearClicked(); EVisibility GetClassPreviewVisibility() const; TSharedRef GetClassPreview(); - TSharedRef OnGeneratePreviewRow(TSharedPtr Class, const TSharedRef& OwnerTable); + TSharedRef OnGeneratePreviewRow( + TSharedPtr Class, const TSharedRef& OwnerTable); void RefreshClassList(); }; - diff --git a/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h b/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h index 91f3a1d..e76d367 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h +++ b/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h @@ -1,17 +1,18 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "CoreMinimal.h" -#include "Widgets/DeclarativeSyntaxSupport.h" +#include "EdGraphSchema_K2.h" #include "EdGraphUtilities.h" #include "GameplayTagContainer.h" -#include "EdGraphSchema_K2.h" -#include "SGraphPin.h" -#include "SClassFilterGraphPin.h" #include "Misc/ClassFilter.h" +#include "SClassFilterGraphPin.h" +#include "SGraphPin.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + -class FSEClassFilterGraphPanelPinFactory: public FGraphPanelPinFactory +class FSEClassFilterGraphPanelPinFactory : public FGraphPanelPinFactory { virtual TSharedPtr CreatePin(class UEdGraphPin* InPin) const override { diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp index 58c4e5a..b675af3 100644 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp @@ -1,14 +1,18 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Customizations/SEComponentClassFilterCustomization.h" + #include "PropertyHandle.h" + #define LOCTEXT_NAMESPACE "FSEComponentClassFilterCustomization" -TSharedPtr FSEComponentClassFilterCustomization::GetFilterHandle(TSharedRef StructPropertyHandle) +TSharedPtr FSEComponentClassFilterCustomization::GetFilterHandle( + TSharedRef StructPropertyHandle) { - return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEComponentClassFilter, ClassFilter));; + return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEComponentClassFilter, ClassFilter)); + ; } #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h index a3324fa..16a632f 100644 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h +++ b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "SEClassFilterCustomization.h" @@ -9,19 +9,17 @@ class IPropertyHandle; class FSEComponentClassFilterCustomization : public FSEClassFilterCustomization { public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ + * Creates a new instance. + * + * @return A new struct customization for Factions. + */ static TSharedRef MakeInstance() { return MakeShared(); } protected: - - virtual TSharedPtr GetFilterHandle(TSharedRef StructPropertyHandle) override; + virtual TSharedPtr GetFilterHandle( + TSharedRef StructPropertyHandle) override; }; - diff --git a/Source/Editor/Private/Customizations/SavePresetDetails.cpp b/Source/Editor/Private/Customizations/SavePresetDetails.cpp index 163306f..4dc6dec 100644 --- a/Source/Editor/Private/Customizations/SavePresetDetails.cpp +++ b/Source/Editor/Private/Customizations/SavePresetDetails.cpp @@ -1,14 +1,15 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SavePresetDetails.h" -#include "DetailLayoutBuilder.h" #include "DetailCategoryBuilder.h" +#include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" -#include +#include "SavePreset.h" + #include +#include -#include "SavePreset.h" #define LOCTEXT_NAMESPACE "FSavePresetDetails" @@ -39,23 +40,22 @@ void FSavePresetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) Category.AddProperty(TEXT("MultithreadedSerialization")); Category.AddProperty(TEXT("FrameSplittedSerialization")); Category.AddCustomRow(LOCTEXT("AsyncWarning", "Asynchronous Warning")) - .Visibility({ this, &FSavePresetDetails::GetWarningVisibility }) - .ValueContent() - .MinDesiredWidth(300.f) - .MaxDesiredWidth(400.f) - [ - SNew(SBorder) - .Padding(2.f) - .BorderImage(FAppStyle::GetBrush("ErrorReporting.EmptyBox")) - .BorderBackgroundColor(this, &FSavePresetDetails::GetWarningColor) - [ - SNew(STextBlock) - .Text(LOCTEXT("AsyncWarningText", "WARNING: Frame-splitted loading or saving is not recommended while using Level Streaming or World Composition")) - .AutoWrapText(true) - ] - ]; - - Category.AddProperty(TEXT("MaxFrameMs")).EditCondition({ this, &FSavePresetDetails::CanEditAsynchronous }, nullptr); + .Visibility({this, &FSavePresetDetails::GetWarningVisibility}) + .ValueContent() + .MinDesiredWidth(300.f) + .MaxDesiredWidth( + 400.f)[SNew(SBorder) + .Padding(2.f) + .BorderImage(FAppStyle::GetBrush("ErrorReporting.EmptyBox")) + .BorderBackgroundColor(this, &FSavePresetDetails::GetWarningColor) + [SNew(STextBlock) + .Text(LOCTEXT("AsyncWarningText", + "WARNING: Frame-splitted loading or saving is not recommended " + "while using Level Streaming or World Composition")) + .AutoWrapText(true)]]; + + Category.AddProperty(TEXT("MaxFrameMs")) + .EditCondition({this, &FSavePresetDetails::CanEditAsynchronous}, nullptr); } DetailBuilder.EditCategory(TEXT("Level Streaming")); @@ -64,14 +64,15 @@ void FSavePresetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) FSlateColor FSavePresetDetails::GetWarningColor() const { - return FLinearColor{ FColor{ 234, 220, 25, 128 } }; + return FLinearColor{FColor{234, 220, 25, 128}}; } EVisibility FSavePresetDetails::GetWarningVisibility() const { if (Settings.IsValid()) { - return Settings->GetFrameSplitSerialization() == ESaveASyncMode::OnlySync ? EVisibility::Collapsed : EVisibility::Visible; + return Settings->GetFrameSplitSerialization() == ESaveASyncMode::OnlySync ? EVisibility::Collapsed + : EVisibility::Visible; } return EVisibility::Collapsed; } diff --git a/Source/Editor/Private/Customizations/SavePresetDetails.h b/Source/Editor/Private/Customizations/SavePresetDetails.h index 7bcc334..13ef8a9 100644 --- a/Source/Editor/Private/Customizations/SavePresetDetails.h +++ b/Source/Editor/Private/Customizations/SavePresetDetails.h @@ -1,15 +1,16 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "CoreMinimal.h" +#include "EditorUndoClient.h" #include "IDetailCustomization.h" -#include "Layout/Visibility.h" -#include "Widgets/Views/SListView.h" -#include "Widgets/Input/SEditableTextBox.h" #include "Input/Reply.h" +#include "Layout/Visibility.h" #include "PropertyHandle.h" -#include "EditorUndoClient.h" +#include "Widgets/Input/SEditableTextBox.h" +#include "Widgets/Views/SListView.h" + class IDetailsView; class IDetailLayoutBuilder; @@ -19,12 +20,10 @@ class USavePreset; class FSavePresetDetails : public IDetailCustomization { public: - /** Makes a new instance of this detail layout class for a specific detail view requesting it */ static TSharedRef MakeInstance(); private: - /** IDetailCustomization interface */ virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override; diff --git a/Source/Editor/Private/SaveExtensionEditor.cpp b/Source/Editor/Private/SaveExtensionEditor.cpp index 016708a..29c89db 100644 --- a/Source/Editor/Private/SaveExtensionEditor.cpp +++ b/Source/Editor/Private/SaveExtensionEditor.cpp @@ -1,18 +1,17 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SaveExtensionEditor.h" -#include "Kismet2/KismetEditorUtilities.h" - -#include "Asset/AssetTypeAction_SlotInfo.h" -#include "Asset/AssetTypeAction_SlotData.h" #include "Asset/AssetTypeAction_SavePreset.h" - -#include "Customizations/SavePresetDetails.h" +#include "Asset/AssetTypeAction_SaveSlot.h" +#include "Asset/AssetTypeAction_SaveSlotData.h" +#include "Customizations/SEActorClassFilterCustomization.h" #include "Customizations/SEClassFilterCustomization.h" #include "Customizations/SEClassFilterGraphPanelPinFactory.h" -#include "Customizations/SEActorClassFilterCustomization.h" #include "Customizations/SEComponentClassFilterCustomization.h" +#include "Customizations/SavePresetDetails.h" +#include "Kismet2/KismetEditorUtilities.h" + #define LOCTEXT_NAMESPACE "SaveExtensionEditor" @@ -20,10 +19,11 @@ void FSaveExtensionEditor::StartupModule() { IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); - AssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName("SaveExtension"), LOCTEXT("Category", "Save Extension")); + AssetCategory = AssetTools.RegisterAdvancedAssetCategory( + FName("SaveExtension"), LOCTEXT("Category", "Save Extension")); - AssetTools.RegisterAssetTypeActions(MakeShared()); - AssetTools.RegisterAssetTypeActions(MakeShared()); + AssetTools.RegisterAssetTypeActions(MakeShared()); + AssetTools.RegisterAssetTypeActions(MakeShared()); AssetTools.RegisterAssetTypeActions(MakeShared()); RegisterPropertyTypeCustomizations(); @@ -45,35 +45,46 @@ void FSaveExtensionEditor::ShutdownModule() void FSaveExtensionEditor::RegisterPropertyTypeCustomizations() { - RegisterCustomClassLayout("SavePreset", FOnGetDetailCustomizationInstance::CreateStatic(&FSavePresetDetails::MakeInstance)); - - RegisterCustomPropertyTypeLayout("SEClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout("SEActorClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEActorClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout("SEComponentClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEComponentClassFilterCustomization::MakeInstance)); - //RegisterCustomPropertyTypeLayout("SavePreset", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSavePresetCustomization::MakeInstance)); + RegisterCustomClassLayout( + "SavePreset", FOnGetDetailCustomizationInstance::CreateStatic(&FSavePresetDetails::MakeInstance)); + + RegisterCustomPropertyTypeLayout("SEClassFilter", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); + RegisterCustomPropertyTypeLayout( + "SEActorClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( + &FSEActorClassFilterCustomization::MakeInstance)); + RegisterCustomPropertyTypeLayout( + "SEComponentClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( + &FSEComponentClassFilterCustomization::MakeInstance)); + // RegisterCustomPropertyTypeLayout("SavePreset", + // FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSavePresetCustomization::MakeInstance)); RegisterCustomPinFactory(); } -void FSaveExtensionEditor::RegisterCustomClassLayout(FName ClassName, FOnGetDetailCustomizationInstance DetailLayoutDelegate) +void FSaveExtensionEditor::RegisterCustomClassLayout( + FName ClassName, FOnGetDetailCustomizationInstance DetailLayoutDelegate) { check(ClassName != NAME_None); static FName PropertyEditor("PropertyEditor"); - FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(PropertyEditor); + FPropertyEditorModule& PropertyModule = + FModuleManager::GetModuleChecked(PropertyEditor); PropertyModule.RegisterCustomClassLayout(ClassName, DetailLayoutDelegate); } -void FSaveExtensionEditor::RegisterCustomPropertyTypeLayout(FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate) +void FSaveExtensionEditor::RegisterCustomPropertyTypeLayout( + FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate) { check(PropertyTypeName != NAME_None); static FName PropertyEditor("PropertyEditor"); - FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(PropertyEditor); + FPropertyEditorModule& PropertyModule = + FModuleManager::GetModuleChecked(PropertyEditor); PropertyModule.RegisterCustomPropertyTypeLayout(PropertyTypeName, PropertyTypeLayoutDelegate); } -template +template void FSaveExtensionEditor::RegisterCustomPinFactory() { TSharedPtr PinFactory = MakeShareable(new T()); diff --git a/Source/Editor/Private/SaveExtensionEditor.h b/Source/Editor/Private/SaveExtensionEditor.h index 50cdcd1..d74c899 100644 --- a/Source/Editor/Private/SaveExtensionEditor.h +++ b/Source/Editor/Private/SaveExtensionEditor.h @@ -1,45 +1,51 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include -#include -#include +#include "ISaveExtensionEditor.h" +#include "SaveActorEditorTabSummoner.h" + #include #include #include #include +#include +#include +#include -#include "SaveActorEditorTabSummoner.h" -#include "ISaveExtensionEditor.h" -/** Shared class type that ensures safe binding to RegisterBlueprintEditorTab through an SP binding without interfering with module ownership semantics */ -class FSaveActorEditorTabBinding - : public TSharedFromThis +/** Shared class type that ensures safe binding to RegisterBlueprintEditorTab through an SP binding without + * interfering with module ownership semantics */ +class FSaveActorEditorTabBinding : public TSharedFromThis { public: - FSaveActorEditorTabBinding() { - FBlueprintEditorModule& BlueprintEditorModule = FModuleManager::LoadModuleChecked("Kismet"); - BlueprintEditorTabSpawnerHandle = BlueprintEditorModule.OnRegisterTabsForEditor().AddRaw(this, &FSaveActorEditorTabBinding::RegisterBlueprintEditorTab); - BlueprintEditorLayoutExtensionHandle = BlueprintEditorModule.OnRegisterLayoutExtensions().AddRaw(this, &FSaveActorEditorTabBinding::RegisterBlueprintEditorLayout); + FBlueprintEditorModule& BlueprintEditorModule = + FModuleManager::LoadModuleChecked("Kismet"); + BlueprintEditorTabSpawnerHandle = BlueprintEditorModule.OnRegisterTabsForEditor().AddRaw( + this, &FSaveActorEditorTabBinding::RegisterBlueprintEditorTab); + BlueprintEditorLayoutExtensionHandle = BlueprintEditorModule.OnRegisterLayoutExtensions().AddRaw( + this, &FSaveActorEditorTabBinding::RegisterBlueprintEditorLayout); } void RegisterBlueprintEditorLayout(FLayoutExtender& Extender) { - Extender.ExtendLayout(FBlueprintEditorTabs::MyBlueprintID, ELayoutExtensionPosition::After, FTabManager::FTab(FSaveActorEditorSummoner::TabName, ETabState::OpenedTab)); + Extender.ExtendLayout(FBlueprintEditorTabs::MyBlueprintID, ELayoutExtensionPosition::After, + FTabManager::FTab(FSaveActorEditorSummoner::TabName, ETabState::OpenedTab)); } - void RegisterBlueprintEditorTab(FWorkflowAllowedTabSet& TabFactories, FName InModeName, TSharedPtr BlueprintEditor) + void RegisterBlueprintEditorTab( + FWorkflowAllowedTabSet& TabFactories, FName InModeName, TSharedPtr BlueprintEditor) { TabFactories.RegisterFactory(MakeShared(BlueprintEditor)); } ~FSaveActorEditorTabBinding() { - FBlueprintEditorModule* BlueprintEditorModule = FModuleManager::GetModulePtr("Kismet"); + FBlueprintEditorModule* BlueprintEditorModule = + FModuleManager::GetModulePtr("Kismet"); if (BlueprintEditorModule) { BlueprintEditorModule->OnRegisterTabsForEditor().Remove(BlueprintEditorTabSpawnerHandle); @@ -48,7 +54,6 @@ class FSaveActorEditorTabBinding } private: - /** Delegate binding handle for FBlueprintEditorModule::OnRegisterTabsForEditor */ FDelegateHandle BlueprintEditorTabSpawnerHandle, BlueprintEditorLayoutExtensionHandle; }; @@ -56,39 +61,40 @@ class FSaveActorEditorTabBinding class FSaveExtensionEditor : public ISaveExtensionEditor { public: - virtual void StartupModule() override; virtual void ShutdownModule() override; private: - void RegisterPropertyTypeCustomizations(); /** - * Registers a custom class - * - * @param ClassName The class name to register for property customization - * @param DetailLayoutDelegate The delegate to call to get the custom detail layout instance - */ + * Registers a custom class + * + * @param ClassName The class name to register for property customization + * @param DetailLayoutDelegate The delegate to call to get the custom detail layout instance + */ void RegisterCustomClassLayout(FName ClassName, FOnGetDetailCustomizationInstance DetailLayoutDelegate); /** - * Registers a custom struct - * - * @param StructName The name of the struct to register for property customization - * @param StructLayoutDelegate The delegate to call to get the custom detail layout instance - */ - void RegisterCustomPropertyTypeLayout(FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate); - - template + * Registers a custom struct + * + * @param StructName The name of the struct to register for property customization + * @param StructLayoutDelegate The delegate to call to get the custom detail layout instance + */ + void RegisterCustomPropertyTypeLayout( + FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate); + + template void RegisterCustomPinFactory(); - //Simplify Registering generated default events -#define RegisterDefaultEventChecked(Class, FuncName) \ - (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent(this, Class::StaticClass(), GET_FUNCTION_NAME_CHECKED(Class, FuncName))) + // Simplify Registering generated default events +#define RegisterDefaultEventChecked(Class, FuncName) \ + (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent( \ + this, Class::StaticClass(), GET_FUNCTION_NAME_CHECKED(Class, FuncName))) -#define RegisterDefaultEvent(Class, FuncName) \ - (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent(this, Class::StaticClass(), FName(TEXT(#FuncName)))) +#define RegisterDefaultEvent(Class, FuncName) \ + (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent( \ + this, Class::StaticClass(), FName(TEXT(#FuncName)))) TSharedPtr BlueprintEditorTabBinding; @@ -96,5 +102,3 @@ class FSaveExtensionEditor : public ISaveExtensionEditor /** All created pin factories. Cached here so that we can unregister them during shutdown. */ TArray> CreatedPinFactories; }; - - diff --git a/Source/Editor/Public/ISaveExtensionEditor.h b/Source/Editor/Public/ISaveExtensionEditor.h index d4b37eb..4c38e9b 100644 --- a/Source/Editor/Public/ISaveExtensionEditor.h +++ b/Source/Editor/Public/ISaveExtensionEditor.h @@ -1,23 +1,24 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "AssetTypeCategories.h" #include "Modules/ModuleManager.h" -#include "AssetTypeCategories.h" class ISaveExtensionEditor : public IModuleInterface { public: - EAssetTypeCategories::Type AssetCategory; - static inline ISaveExtensionEditor &Get() { + static inline ISaveExtensionEditor& Get() + { return FModuleManager::LoadModuleChecked("SaveExtensionEditor"); } - static inline bool IsAvailable() { + static inline bool IsAvailable() + { return FModuleManager::Get().IsModuleLoaded("SaveExtensionEditor"); } }; diff --git a/Source/Editor/SaveExtensionEditor.Build.cs b/Source/Editor/SaveExtensionEditor.Build.cs index 9cd7e34..f8354f8 100644 --- a/Source/Editor/SaveExtensionEditor.Build.cs +++ b/Source/Editor/SaveExtensionEditor.Build.cs @@ -5,12 +5,14 @@ namespace UnrealBuildTool.Rules { - public class SaveExtensionEditor : ModuleRules { - public SaveExtensionEditor(ReadOnlyTargetRules Target) : base(Target) { + public class SaveExtensionEditor : ModuleRules + { + public SaveExtensionEditor(ReadOnlyTargetRules Target) : base(Target) + { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - bEnforceIWYU = true; + IWYUSupport = IWYUSupport.Full; - PublicDependencyModuleNames.AddRange( new string[] + PublicDependencyModuleNames.AddRange(new string[] { "Core", "Engine", @@ -19,7 +21,7 @@ public SaveExtensionEditor(ReadOnlyTargetRules Target) : base(Target) { "SaveExtension" }); - PrivateDependencyModuleNames.AddRange( new string[] + PrivateDependencyModuleNames.AddRange(new string[] { "AssetTools", "Projects", @@ -30,7 +32,8 @@ public SaveExtensionEditor(ReadOnlyTargetRules Target) : base(Target) { "EditorStyle", "ClassViewer", "BlueprintGraph", - "GraphEditor" + "GraphEditor", + "PropertyEditor" }); } } diff --git a/Source/SaveExtension/Private/FileAdapter.cpp b/Source/SaveExtension/Private/FileAdapter.cpp index e4290b9..d5aa543 100644 --- a/Source/SaveExtension/Private/FileAdapter.cpp +++ b/Source/SaveExtension/Private/FileAdapter.cpp @@ -1,22 +1,22 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "FileAdapter.h" -#include -#include +#include "Multithreading/SaveFileTask.h" +#include "SavePreset.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" + +#include +#include +#include #include #include -#include -#include -#include - -#include "SavePreset.h" -#include "SlotInfo.h" -#include "SlotData.h" -#include "Multithreading/SaveFileTask.h" +#include +#include -static const int SE_SAVEGAME_FILE_TYPE_TAG = 0x0001; // "sAvG" +static const int SE_SAVEGAME_FILE_TYPE_TAG = 0x0001; // "sAvG" struct FSaveGameFileVersion { @@ -83,7 +83,7 @@ void FSaveFile::Read(FScopedFileReader& Reader, bool bSkipData) Empty(); FArchive& Ar = Reader.GetArchive(); - { // Header information + { // Header information Ar << FileTypeTag; if (FileTypeTag != SE_SAVEGAME_FILE_TYPE_TAG) { @@ -104,7 +104,8 @@ void FSaveFile::Read(FScopedFileReader& Reader, bool bSkipData) if (SaveGameFileVersion >= FSaveGameFileVersion::AddedCustomVersions) { Ar << CustomVersionFormat; - CustomVersions.Serialize(Ar, static_cast(CustomVersionFormat)); + CustomVersions.Serialize( + Ar, static_cast(CustomVersionFormat)); Ar.SetCustomVersions(CustomVersions); } } @@ -113,13 +114,13 @@ void FSaveFile::Read(FScopedFileReader& Reader, bool bSkipData) Ar << InfoBytes; Ar << DataClassName; - if(bSkipData || DataClassName.IsEmpty()) + if (bSkipData || DataClassName.IsEmpty()) { return; } Ar << bIsDataCompressed; - if(bIsDataCompressed) + if (bIsDataCompressed) { TArray CompressedDataBytes; Ar << CompressedDataBytes; @@ -149,26 +150,27 @@ void FSaveFile::Write(FScopedFileWriter& Writer, bool bCompressData) bIsDataCompressed = bCompressData; FArchive& Ar = Writer.GetArchive(); - { // Header information + { // Header information Ar << FileTypeTag; Ar << SaveGameFileVersion; Ar << PackageFileUEVersion; Ar << SavedEngineVersion; Ar << CustomVersionFormat; - CustomVersions.Serialize(Ar, static_cast(CustomVersionFormat)); + CustomVersions.Serialize( + Ar, static_cast(CustomVersionFormat)); } Ar << InfoClassName; Ar << InfoBytes; Ar << DataClassName; - if(!DataClassName.IsEmpty()) + if (!DataClassName.IsEmpty()) { Ar << bIsDataCompressed; - if(bIsDataCompressed) + if (bIsDataCompressed) { TArray CompressedDataBytes; - { // Compression + { // Compression TRACE_CPUPROFILER_EVENT_SCOPE(Compression); // Compress Object data FArchiveSaveCompressedProxy Compressor(CompressedDataBytes, NAME_Zlib); @@ -185,7 +187,7 @@ void FSaveFile::Write(FScopedFileWriter& Writer, bool bCompressData) Ar.Close(); } -void FSaveFile::SerializeInfo(USlotInfo* SlotInfo) +void FSaveFile::SerializeInfo(USaveSlot* SlotInfo) { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::SerializeInfo); check(SlotInfo); @@ -196,7 +198,7 @@ void FSaveFile::SerializeInfo(USlotInfo* SlotInfo) FObjectAndNameAsStringProxyArchive Ar(BytesWriter, false); SlotInfo->Serialize(Ar); } -void FSaveFile::SerializeData(USlotData* SlotData) +void FSaveFile::SerializeData(USaveSlotData* SlotData) { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::SerializeData); check(SlotData); @@ -208,23 +210,23 @@ void FSaveFile::SerializeData(USlotData* SlotData) SlotData->Serialize(Ar); } -USlotInfo* FSaveFile::CreateAndDeserializeInfo(const UObject* Outer) const +USaveSlot* FSaveFile::CreateAndDeserializeInfo(const UObject* Outer) const { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeInfo); - UObject* Object = nullptr; - FFileAdapter::DeserializeObject(Object, InfoClassName, Outer, InfoBytes); - return Cast(Object); + UObject* Slot = nullptr; + FFileAdapter::DeserializeObject(Slot, InfoClassName, Outer, InfoBytes); + return Cast(Slot); } -USlotData* FSaveFile::CreateAndDeserializeData(const UObject* Outer) const +void FSaveFile::CreateAndDeserializeData(USaveSlot* Slot) const { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeData); - UObject* Object = nullptr; - FFileAdapter::DeserializeObject(Object, DataClassName, Outer, DataBytes); - return Cast(Object); + UObject* Data = nullptr; + FFileAdapter::DeserializeObject(Data, DataClassName, Slot, DataBytes); + Slot->AssignData(Cast(Data)); } -bool FFileAdapter::SaveFile(FStringView SlotName, USlotInfo* Info, USlotData* Data, const bool bUseCompression) +bool FFileAdapter::SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression) { TRACE_CPUPROFILER_EVENT_SCOPE(FFileAdapter::SaveFile); @@ -233,38 +235,41 @@ bool FFileAdapter::SaveFile(FStringView SlotName, USlotInfo* Info, USlotData* Da return false; } - if (!ensureMsgf(Info, TEXT("Info object must be valid")) || - !ensureMsgf(Data, TEXT("Data object must be valid"))) + if (!ensureMsgf(Slot, TEXT("Slot object must be valid")) || + !ensureMsgf(Slot->GetData(), TEXT("Slot Data object must be valid"))) { return false; } FScopedFileWriter FileWriter(GetSlotPath(SlotName)); - if(FileWriter.IsValid()) + if (FileWriter.IsValid()) { FSaveFile File{}; - File.SerializeInfo(Info); - File.SerializeData(Data); + File.SerializeInfo(Slot); + File.SerializeData(Slot->GetData()); File.Write(FileWriter, bUseCompression); return !FileWriter.IsError(); } return false; } -bool FFileAdapter::LoadFile(FStringView SlotName, USlotInfo*& Info, USlotData*& Data, bool bLoadData, const UObject* Outer) +USaveSlot* FFileAdapter::LoadFile(FStringView SlotName, bool bLoadData, const UObject* Outer) { TRACE_CPUPROFILER_EVENT_SCOPE(FFileAdapter::LoadFile); FScopedFileReader Reader(GetSlotPath(SlotName)); - if(Reader.IsValid()) + if (Reader.IsValid()) { FSaveFile File{}; File.Read(Reader, !bLoadData); - Info = File.CreateAndDeserializeInfo(Outer); - Data = File.CreateAndDeserializeData(Outer); - return true; + USaveSlot* Slot = File.CreateAndDeserializeInfo(Outer); + if (bLoadData) + { + File.CreateAndDeserializeData(Slot); + } + return Slot; } - return false; + return nullptr; } bool FFileAdapter::DeleteFile(FStringView SlotName) @@ -272,7 +277,7 @@ bool FFileAdapter::DeleteFile(FStringView SlotName) return IFileManager::Get().Delete(*GetSlotPath(SlotName), true, false, true); } -bool FFileAdapter::DoesFileExist(FStringView SlotName) +bool FFileAdapter::FileExists(FStringView SlotName) { return IFileManager::Get().FileSize(*GetSlotPath(SlotName)) >= 0; } @@ -293,7 +298,8 @@ FString FFileAdapter::GetThumbnailPath(FStringView SlotName) return GetSaveFolder() / FString::Printf(TEXT("%s.png"), SlotName.GetData()); } -void FFileAdapter::DeserializeObject(UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes) +void FFileAdapter::DeserializeObject( + UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes) { if (ClassName.IsEmpty() || Bytes.Num() <= 0) { @@ -310,9 +316,9 @@ void FFileAdapter::DeserializeObject(UObject*& Object, FStringView ClassName, co return; } - if(!Object) + if (!Object) { - if(!Outer) + if (!Outer) { Outer = GetTransientPackage(); } @@ -320,14 +326,14 @@ void FFileAdapter::DeserializeObject(UObject*& Object, FStringView ClassName, co Object = NewObject(const_cast(Outer), ObjectClass); } // Can only reuse object if class matches - else if(Object->GetClass() != ObjectClass) + else if (Object->GetClass() != ObjectClass) { return; } - if(Object) + if (Object) { - FMemoryReader Reader{ Bytes }; + FMemoryReader Reader{Bytes}; FObjectAndNameAsStringProxyArchive Ar(Reader, true); Object->Serialize(Ar); } diff --git a/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp b/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp index 0b984f3..b600bbe 100644 --- a/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp +++ b/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp @@ -1,12 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LatentActions/DeleteSlotsAction.h" #include "SaveManager.h" -#include "SlotInfo.h" +#include "SaveSlot.h" -FDeleteSlotsAction::FDeleteSlotsAction(USaveManager* Manager, EDeleteSlotsResult& OutResult, const FLatentActionInfo& LatentInfo) +FDeleteSlotsAction::FDeleteSlotsAction( + USaveManager* Manager, EDeleteSlotsResult& OutResult, const FLatentActionInfo& LatentInfo) : Result(OutResult) , bFinished(false) , ExecutionFunction(LatentInfo.ExecutionFunction) diff --git a/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp b/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp index 4ecd87d..da32ab2 100644 --- a/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp +++ b/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp @@ -1,18 +1,21 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LatentActions/LoadGameAction.h" + #include "SaveManager.h" +#include "SaveSlot.h" #include "Serialization/SlotDataTask_Loader.h" -#include "SlotInfo.h" -FLoadGameAction::FLoadGameAction(USaveManager* Manager, FName SlotName, ELoadGameResult& OutResult, const FLatentActionInfo& LatentInfo) +FLoadGameAction::FLoadGameAction( + USaveManager* Manager, FName SlotName, ELoadGameResult& OutResult, const FLatentActionInfo& LatentInfo) : Result(OutResult) , ExecutionFunction(LatentInfo.ExecutionFunction) , OutputLink(LatentInfo.Linkage) , CallbackTarget(LatentInfo.CallbackTarget) { - const bool bStarted = Manager->LoadSlot(SlotName, FOnGameLoaded::CreateRaw(this, &FLoadGameAction::OnLoadFinished)); + const bool bStarted = + Manager->LoadSlot(SlotName, FOnGameLoaded::CreateRaw(this, &FLoadGameAction::OnLoadFinished)); if (!bStarted) { Result = ELoadGameResult::Failed; @@ -21,10 +24,11 @@ FLoadGameAction::FLoadGameAction(USaveManager* Manager, FName SlotName, ELoadGam void FLoadGameAction::UpdateOperation(FLatentResponse& Response) { - Response.FinishAndTriggerIf(Result != ELoadGameResult::Loading, ExecutionFunction, OutputLink, CallbackTarget); + Response.FinishAndTriggerIf( + Result != ELoadGameResult::Loading, ExecutionFunction, OutputLink, CallbackTarget); } -void FLoadGameAction::OnLoadFinished(USlotInfo* SavedSlot) +void FLoadGameAction::OnLoadFinished(USaveSlot* SavedSlot) { Result = SavedSlot ? ELoadGameResult::Continue : ELoadGameResult::Failed; } diff --git a/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp b/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp index 645d5d6..e8ada39 100644 --- a/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp +++ b/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp @@ -1,11 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LatentActions/LoadInfosAction.h" + #include "SaveManager.h" -#include "SlotInfo.h" +#include "SaveSlot.h" -FLoadInfosAction::FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, TArray& InSlotInfos, ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo) +FLoadInfosAction::FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, + TArray& InSlotInfos, ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo) : Result(OutResult) , SlotInfos(InSlotInfos) , bFinished(false) @@ -13,14 +15,14 @@ FLoadInfosAction::FLoadInfosAction(USaveManager* Manager, const bool bSortByRece , OutputLink(LatentInfo.Linkage) , CallbackTarget(LatentInfo.CallbackTarget) { - Manager->LoadAllSlotInfos(bSortByRecent, FOnSlotInfosLoaded::CreateLambda([this](const TArray& Results) { - SlotInfos = Results; - bFinished = true; - })); + Manager->LoadAllSlotInfos( + bSortByRecent, FOnSlotInfosLoaded::CreateLambda([this](const TArray& Results) { + SlotInfos = Results; + bFinished = true; + })); } void FLoadInfosAction::UpdateOperation(FLatentResponse& Response) { Response.FinishAndTriggerIf(bFinished, ExecutionFunction, OutputLink, CallbackTarget); - } diff --git a/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp b/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp index 0220c60..1746e33 100644 --- a/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp +++ b/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp @@ -1,17 +1,21 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LatentActions/SaveGameAction.h" + #include "SaveManager.h" -#include "SlotInfo.h" +#include "SaveSlot.h" -FSaveGameAction::FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& OutResult, const FLatentActionInfo& LatentInfo) +FSaveGameAction::FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, + bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& OutResult, + const FLatentActionInfo& LatentInfo) : Result(OutResult) , ExecutionFunction(LatentInfo.ExecutionFunction) , OutputLink(LatentInfo.Linkage) , CallbackTarget(LatentInfo.CallbackTarget) { - const bool bStarted = Manager->SaveSlot(SlotName, bOverrideIfNeeded, bScreenshot, Size, FOnGameSaved::CreateRaw(this, &FSaveGameAction::OnSaveFinished)); + const bool bStarted = Manager->SaveSlot(SlotName, bOverrideIfNeeded, bScreenshot, Size, + FOnGameSaved::CreateRaw(this, &FSaveGameAction::OnSaveFinished)); if (!bStarted) { @@ -21,10 +25,11 @@ FSaveGameAction::FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOv void FSaveGameAction::UpdateOperation(FLatentResponse& Response) { - Response.FinishAndTriggerIf(Result != ESaveGameResult::Saving, ExecutionFunction, OutputLink, CallbackTarget); + Response.FinishAndTriggerIf( + Result != ESaveGameResult::Saving, ExecutionFunction, OutputLink, CallbackTarget); } -void FSaveGameAction::OnSaveFinished(USlotInfo* SavedSlot) +void FSaveGameAction::OnSaveFinished(USaveSlot* SavedSlot) { Result = SavedSlot ? ESaveGameResult::Continue : ESaveGameResult::Failed; } diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index c665d00..e6c36c0 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LevelFilter.h" @@ -6,7 +6,7 @@ ///////////////////////////////////////////////////// // USaveDataTask -const FName FSELevelFilter::TagNoTransform { "!SaveTransform" }; -const FName FSELevelFilter::TagNoPhysics { "!SavePhysics" }; -const FName FSELevelFilter::TagNoTags { "!SaveTags" }; -const FName FSELevelFilter::TagTransform { "SaveTransform" }; +const FName FSELevelFilter::TagNoTransform{"!SaveTransform"}; +const FName FSELevelFilter::TagNoPhysics{"!SavePhysics"}; +const FName FSELevelFilter::TagNoTags{"!SaveTags"}; +const FName FSELevelFilter::TagTransform{"SaveTransform"}; diff --git a/Source/SaveExtension/Private/LevelStreamingNotifier.cpp b/Source/SaveExtension/Private/LevelStreamingNotifier.cpp index bd8d261..9f14565 100644 --- a/Source/SaveExtension/Private/LevelStreamingNotifier.cpp +++ b/Source/SaveExtension/Private/LevelStreamingNotifier.cpp @@ -1,3 +1,3 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LevelStreamingNotifier.h" diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index f4b0394..74c1ffd 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -1,12 +1,12 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "LifetimeComponent.h" + #include "Serialization/MTTask.h" -ULifetimeComponent::ULifetimeComponent() - : Super() -{} + +ULifetimeComponent::ULifetimeComponent() : Super() {} void ULifetimeComponent::BeginPlay() { @@ -25,7 +25,8 @@ void ULifetimeComponent::BeginPlay() } else { - UE_LOG(LogSaveExtension, Error, TEXT("LifetimeComponent couldnt find a SaveManager. It will do nothing.")) + UE_LOG(LogSaveExtension, Error, + TEXT("LifetimeComponent couldnt find a SaveManager. It will do nothing.")) } } diff --git a/Source/SaveExtension/Private/Misc/ClassFilter.cpp b/Source/SaveExtension/Private/Misc/ClassFilter.cpp index ce01946..618a64a 100644 --- a/Source/SaveExtension/Private/Misc/ClassFilter.cpp +++ b/Source/SaveExtension/Private/Misc/ClassFilter.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Misc/ClassFilter.h" @@ -6,10 +6,7 @@ #include -FSEClassFilter::FSEClassFilter(UClass* BaseClass) - : BaseClass{ BaseClass } - , IgnoredClasses {} -{} +FSEClassFilter::FSEClassFilter(UClass* BaseClass) : BaseClass{BaseClass}, IgnoredClasses{} {} void FSEClassFilter::Merge(const FSEClassFilter& Other) { @@ -32,19 +29,19 @@ void FSEClassFilter::BakeAllowedClasses() const TRACE_CPUPROFILER_EVENT_SCOPE(FSEClassFilter::BakeAllowedClasses); BakedAllowedClasses.Empty(); - if(AllowedClasses.Num() <= 0) + if (AllowedClasses.Num() <= 0) { return; } TArray ChildrenOfAllowedClasses; { - TRACE_CPUPROFILER_EVENT_SCOPE(First Pass: Potential classes); - for(auto& AllowedClass : AllowedClasses) + TRACE_CPUPROFILER_EVENT_SCOPE(First Pass : Potential classes); + for (auto& AllowedClass : AllowedClasses) { UClass* const AllowedClassPtr = AllowedClass.Get(); - if(!AllowedClassPtr) + if (!AllowedClassPtr) { continue; } @@ -54,7 +51,7 @@ void FSEClassFilter::BakeAllowedClasses() const } } { - TRACE_CPUPROFILER_EVENT_SCOPE(Second Pass: Bake Classes); + TRACE_CPUPROFILER_EVENT_SCOPE(Second Pass : Bake Classes); for (UClass* Class : ChildrenOfAllowedClasses) { // Iterate parent classes of a class @@ -129,5 +126,5 @@ bool FSEClassFilter::operator==(const FSEClassFilter& Other) const { // Do all classes match? return AllowedClasses.Difference(Other.AllowedClasses).Num() <= 0 && - IgnoredClasses.Difference(Other.IgnoredClasses).Num() <= 0; + IgnoredClasses.Difference(Other.IgnoredClasses).Num() <= 0; } diff --git a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp index 64c7c6a..1c1bf17 100644 --- a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp +++ b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp @@ -1,13 +1,14 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Misc/SlotHelpers.h" -#include -#include #include +#include void FSlotHelpers::FindSlotFileNames(TArray& FoundSlots) { - FFindSlotVisitor Visitor{ FoundSlots }; + FFindSlotVisitor Visitor{FoundSlots}; FPlatformFileManager::Get().GetPlatformFile().IterateDirectory(*FFileAdapter::GetSaveFolder(), Visitor); } @@ -30,4 +31,3 @@ bool FSlotHelpers::FFindSlotVisitor::Visit(const TCHAR* FilenameOrDirectory, boo } return true; } - diff --git a/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp b/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp index 5e36387..06b5722 100644 --- a/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp @@ -1,21 +1,21 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Multithreading/DeleteSlotsTask.h" -#include - #include "FileAdapter.h" -#include "SavePreset.h" -#include "SaveManager.h" -#include "Misc/SlotHelpers.h" #include "HAL/FileManager.h" +#include "Misc/SlotHelpers.h" +#include "SaveManager.h" +#include "SavePreset.h" + +#include + -FDeleteSlotsTask::FDeleteSlotsTask(const USaveManager* InManager, FName SlotName) - : Manager(InManager) +FDeleteSlotsTask::FDeleteSlotsTask(const USaveManager* InManager, FName SlotName) : Manager(InManager) { check(Manager); - if(!SlotName.IsNone()) + if (!SlotName.IsNone()) { SpecificSlotName = SlotName.ToString(); } diff --git a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp index b8d947e..30fc7fa 100644 --- a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Multithreading/LoadFileTask.h" diff --git a/Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp index 79c95d1..dd72894 100644 --- a/Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp @@ -1,13 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Multithreading/LoadSlotInfosTask.h" -#include - #include "FileAdapter.h" -#include "SavePreset.h" -#include "SaveManager.h" #include "Misc/SlotHelpers.h" +#include "SaveManager.h" +#include "SavePreset.h" + +#include void FLoadSlotInfosTask::DoWork() @@ -19,7 +19,7 @@ void FLoadSlotInfosTask::DoWork() TArray FileNames; const bool bLoadingSingleInfo = !SlotName.IsNone(); - if(bLoadingSingleInfo) + if (bLoadingSingleInfo) { FileNames.Add(SlotName.ToString()); } @@ -34,7 +34,7 @@ void FLoadSlotInfosTask::DoWork() { // Load all files FScopedFileReader Reader(FFileAdapter::GetSlotPath(FileName)); - if(Reader.IsValid()) + if (Reader.IsValid()) { auto& File = LoadedFiles.AddDefaulted_GetRef(); File.Read(Reader, true); @@ -50,15 +50,15 @@ void FLoadSlotInfosTask::DoWork() if (!bLoadingSingleInfo && bSortByRecent) { - LoadedSlots.Sort([](const USlotInfo& A, const USlotInfo& B) { - return A.SaveDate > B.SaveDate; + LoadedSlots.Sort([](const USaveSlot& A, const USaveSlot& B) { + return A.Stats.SaveDate > B.Stats.SaveDate; }); } } void FLoadSlotInfosTask::AfterFinish() { - for(auto& Slot : LoadedSlots) + for (auto& Slot : LoadedSlots) { Slot->ClearInternalFlags(EInternalObjectFlags::Async); } diff --git a/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp b/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp index d9a3695..bf243eb 100644 --- a/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Multithreading/SaveFileTask.h" diff --git a/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp b/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp index a1b5d43..e29b16f 100644 --- a/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp +++ b/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp @@ -1,3 +1,3 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Multithreading/ScopedTaskManager.h" diff --git a/Source/SaveExtension/Private/SaveExtension.cpp b/Source/SaveExtension/Private/SaveExtension.cpp index 385ce26..7786a64 100644 --- a/Source/SaveExtension/Private/SaveExtension.cpp +++ b/Source/SaveExtension/Private/SaveExtension.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SaveExtension.h" diff --git a/Source/SaveExtension/Private/SaveExtension.h b/Source/SaveExtension/Private/SaveExtension.h index faac276..6b88367 100644 --- a/Source/SaveExtension/Private/SaveExtension.h +++ b/Source/SaveExtension/Private/SaveExtension.h @@ -1,22 +1,25 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ISaveExtension.h" #if WITH_EDITORONLY_DATA - #include "ISettingsModule.h" - #include "ISettingsSection.h" - #include "ISettingsContainer.h" +# include "ISettingsContainer.h" +# include "ISettingsModule.h" +# include "ISettingsSection.h" + #endif class FSaveExtension : public ISaveExtension { public: - virtual void StartupModule() override {} virtual void ShutdownModule() override {} - virtual bool SupportsDynamicReloading() override { return true; } + virtual bool SupportsDynamicReloading() override + { + return true; + } }; diff --git a/Source/SaveExtension/Private/SaveExtensionInterface.cpp b/Source/SaveExtension/Private/SaveExtensionInterface.cpp index a1c7425..c562b67 100644 --- a/Source/SaveExtension/Private/SaveExtensionInterface.cpp +++ b/Source/SaveExtension/Private/SaveExtensionInterface.cpp @@ -1,7 +1,7 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SaveExtensionInterface.h" -USaveExtensionInterface::USaveExtensionInterface(const FObjectInitializer &ObjectInitializer) +USaveExtensionInterface::USaveExtensionInterface(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {} diff --git a/Source/SaveExtension/Private/SaveExtensionLibrary.cpp b/Source/SaveExtension/Private/SaveExtensionLibrary.cpp index d07c6f2..b4cd797 100644 --- a/Source/SaveExtension/Private/SaveExtensionLibrary.cpp +++ b/Source/SaveExtension/Private/SaveExtensionLibrary.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SaveExtensionLibrary.h" @@ -6,13 +6,14 @@ #include "HighResScreenshot.h" #include "IImageWrapper.h" #include "IImageWrapperModule.h" -#include "Kismet/KismetSystemLibrary.h" #include "Kismet/GameplayStatics.h" - +#include "Kismet/KismetSystemLibrary.h" #include "SaveManager.h" -/*FSaveTimerHandle USaveExtensionLibrary::SetSaveTimerDelegate(FTimerDynamicDelegate Delegate, float Time, bool bLooping) + +/*FSaveTimerHandle USaveExtensionLibrary::SetSaveTimerDelegate(FTimerDynamicDelegate Delegate, float Time, +bool bLooping) { FTimerHandle Handle { UKismetSystemLibrary::K2_SetTimerDelegate(Delegate, Time, bLooping) }; return FSaveTimerHandle(Handle, Delegate, Time, bLooping); diff --git a/Source/SaveExtension/Private/SaveExtensionLibrary.h b/Source/SaveExtension/Private/SaveExtensionLibrary.h index 2ea550e..bc1eb9b 100644 --- a/Source/SaveExtension/Private/SaveExtensionLibrary.h +++ b/Source/SaveExtension/Private/SaveExtensionLibrary.h @@ -1,11 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "Runtime/Engine/Classes/Kismet/BlueprintFunctionLibrary.h" +#include "SaveSlotData.h" -#include "SlotData.h" -//#include "SaveTimerHandle.h" +// #include "SaveTimerHandle.h" #include "SaveExtensionLibrary.generated.h" @@ -16,7 +16,7 @@ class SAVEEXTENSION_API USaveExtensionLibrary : public UBlueprintFunctionLibrary GENERATED_BODY() public: - - //UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Saveable Timer by Event"), Category = "Utilities|Time") - //static FSaveTimerHandle SetSaveTimerDelegate(FTimerDynamicDelegate Delegate, float Time, bool bLooping); + // UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Saveable Timer by Event"), Category = + // "Utilities|Time") static FSaveTimerHandle SetSaveTimerDelegate(FTimerDynamicDelegate Delegate, float + // Time, bool bLooping); }; diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index a177840..bf6ecd4 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SaveManager.h" @@ -34,15 +34,12 @@ void USaveManager::Initialize(FSubsystemCollectionBase& Collection) FCoreUObjectDelegates::PreLoadMap.AddUObject(this, &USaveManager::OnMapLoadStarted); FCoreUObjectDelegates::PostLoadMapWithWorld.AddUObject(this, &USaveManager::OnMapLoadFinished); - ActivePreset = GetDefault()->CreatePreset(this); - - // AutoLoad - if (GetPreset() && GetPreset()->bAutoLoad) + AssureActiveSlot(); + if (ActiveSlot && ActiveSlot->bLoadOnStart) { ReloadCurrentSlot(); } - TryInstantiateInfo(); UpdateLevelStreamings(); } @@ -52,7 +49,7 @@ void USaveManager::Deinitialize() MTTasks.CancelAll(); - if (GetPreset()->bSaveOnExit) + if (GetActivePreset()->bSaveOnExit) SaveCurrentSlot(); FCoreUObjectDelegates::PreLoadMap.RemoveAll(this); @@ -60,13 +57,13 @@ void USaveManager::Deinitialize() FGameDelegates::Get().GetEndPlayMapDelegate().RemoveAll(this); } -bool USaveManager::SaveSlot( - FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) +bool USaveManager::SaveSlot(FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, + const FScreenshotSize Size, FOnGameSaved OnSaved) { if (!CanLoadOrSave()) return false; - const USavePreset* Preset = GetPreset(); + const USavePreset* Preset = GetActivePreset(); if (SlotName.IsNone()) { SELog(Preset, "Can't use an empty slot name to save.", true); @@ -80,10 +77,10 @@ bool USaveManager::SaveSlot( check(World); // Launch task, always fail if it didn't finish or wasn't scheduled - auto* Task = CreateTask() - ->Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) - ->Bind(OnSaved) - ->Start(); + auto* Task = CreateTask() + ->Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) + ->Bind(OnSaved) + ->Start(); return Task->IsSucceeded() || Task->IsScheduled(); } @@ -95,12 +92,9 @@ bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) return false; } - TryInstantiateInfo(); + AssureActiveSlot(); - auto* Task = CreateTask() - ->Setup(SlotName) - ->Bind(OnLoaded) - ->Start(); + auto* Task = CreateTask()->Setup(SlotName)->Bind(OnLoaded)->Start(); return Task->IsSucceeded() || Task->IsScheduled(); } @@ -122,7 +116,7 @@ bool USaveManager::DeleteSlot(FName SlotName) return bSuccess; } -void USaveManager::LoadAllSlotInfos(bool bSortByRecent, FOnSlotInfosLoaded Delegate) +void USaveManager::FindAllSlots(bool bSortByRecent, FOnSlotInfosLoaded Delegate) { MTTasks.CreateTask(this, bSortByRecent, MoveTemp(Delegate)) .OnFinished([](auto& Task) { @@ -131,9 +125,12 @@ void USaveManager::LoadAllSlotInfos(bool bSortByRecent, FOnSlotInfosLoaded Deleg .StartBackgroundTask(); } -void USaveManager::LoadAllSlotInfosSync(bool bSortByRecent, FOnSlotInfosLoaded Delegate) +void USaveManager::FindAllSlotsSync(bool bSortByRecent, TArray& Slots) { - MTTasks.CreateTask(this, bSortByRecent, MoveTemp(Delegate)) + auto Delegate = [](const TArray& FoundSlots) { + Slots = FoundSlots; + }; + MTTasks.CreateTask(this, bSortByRecent, Delegate) .OnFinished([](auto& Task) { Task->AfterFinish(); }) @@ -162,15 +159,15 @@ void USaveManager::BPSaveSlot(FName SlotName, bool bScreenshot, const FScreensho LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) { LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, - new FSaveGameAction(this, SlotName, bOverrideIfNeeded, bScreenshot, Size, Result, LatentInfo)); + new FSaveGameAction( + this, SlotName, bOverrideIfNeeded, bScreenshot, Size, Result, LatentInfo)); } return; } Result = ESaveGameResult::Failed; } -void USaveManager::BPLoadSlot( - FName SlotName, ELoadGameResult& Result, struct FLatentActionInfo LatentInfo) +void USaveManager::BPLoadSlot(FName SlotName, ELoadGameResult& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) { @@ -188,7 +185,7 @@ void USaveManager::BPLoadSlot( Result = ELoadGameResult::Failed; } -void USaveManager::BPLoadAllSlotInfos(const bool bSortByRecent, TArray& SaveInfos, +void USaveManager::BPFindAllSlots(const bool bSortByRecent, TArray& SaveInfos, ELoadInfoResult& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) @@ -219,40 +216,12 @@ void USaveManager::BPDeleteAllSlots(EDeleteSlotsResult& Result, struct FLatentAc bool USaveManager::IsSlotSaved(FName SlotName) const { - return FFileAdapter::DoesFileExist(SlotName.ToString()); -} - -USavePreset* USaveManager::SetActivePreset(TSubclassOf PresetClass) -{ - // We can only change a preset if we have no tasks running - if (HasTasks() || !PresetClass.Get()) - { - return nullptr; - } - - // If We have a preset and its already of the same class, dont do anything - if (ActivePreset && ActivePreset->GetClass() == PresetClass) - { - return nullptr; - } - - ActivePreset = NewObject(this, PresetClass); - return ActivePreset; -} - -const USavePreset* USaveManager::GetPreset() const -{ - if (IsValid(ActivePreset)) - { - return ActivePreset; - } - return GetDefault(); + return FFileAdapter::FileExists(SlotName.ToString()); } bool USaveManager::CanLoadOrSave() { const AGameModeBase* GameMode = UGameplayStatics::GetGameMode(this); - if (GameMode && !GameMode->HasAuthority()) { return false; @@ -261,29 +230,23 @@ bool USaveManager::CanLoadOrSave() return IsValid(GetWorld()); } -void USaveManager::TryInstantiateInfo(bool bForced) +void USaveManager::AssureActiveSlot(bool bForced) { if (IsInSlot() && !bForced) return; - const USavePreset* Preset = GetPreset(); - - UClass* InfoClass = Preset->SlotInfoClass.Get(); - if (!InfoClass) - InfoClass = USlotInfo::StaticClass(); - - UClass* DataClass = Preset->SlotDataClass.Get(); - if (!DataClass) - DataClass = USlotData::StaticClass(); - - CurrentInfo = NewObject(GetTransientPackage(), InfoClass); - CurrentData = NewObject(GetTransientPackage(), DataClass); + UClass* ActiveSlotClass = GetDefault()->ActiveSlot.Get(); + if (!ActiveSlotClass) + { + ActiveSlotClass = USaveSlot::StaticClass(); + } + ActiveSlot = NewObject(this, ActiveSlotClass); } void USaveManager::UpdateLevelStreamings() { UWorld* World = GetWorld(); - if(!World) + if (!World) { return; } @@ -308,45 +271,43 @@ void USaveManager::SerializeStreamingLevel(ULevelStreaming* LevelStreaming) { if (!LevelStreaming->GetLoadedLevel()->bIsBeingRemoved) { - CreateTask()->Setup(LevelStreaming)->Start(); + CreateTask()->Setup(LevelStreaming)->Start(); } } void USaveManager::DeserializeStreamingLevel(ULevelStreaming* LevelStreaming) { - CreateTask()->Setup(LevelStreaming)->Start(); + CreateTask()->Setup(LevelStreaming)->Start(); } -USlotInfo* USaveManager::LoadInfo(FName SlotName) +USaveSlot* USaveManager::LoadInfo(FName SlotName) { if (SlotName.IsNone()) { - SELog(GetPreset(), "Invalid Slot. Cant go under 0 or exceed MaxSlots", true); + SELog(GetActivePreset(), "Invalid Slot. Cant go under 0 or exceed MaxSlots", true); return nullptr; } - auto& Task = MTTasks.CreateTask(this, SlotName) - .OnFinished([](auto& Task) - { - Task->AfterFinish(); - }); + auto& Task = MTTasks.CreateTask(this, SlotName).OnFinished([](auto& Task) { + Task->AfterFinish(); + }); Task.StartSynchronousTask(); check(Task.IsDone()); const auto& Infos = Task->GetLoadedSlots(); - return Infos.Num() > 0? Infos[0] : nullptr; + return Infos.Num() > 0 ? Infos[0] : nullptr; } -USlotDataTask* USaveManager::CreateTask(TSubclassOf TaskType) +USaveSlotDataTask* USaveManager::CreateTask(TSubclassOf TaskType) { - USlotDataTask* Task = NewObject(this, TaskType.Get()); - Task->Prepare(CurrentData, *GetPreset()); + USaveSlotDataTask* Task = NewObject(this, TaskType.Get()); + Task->Prepare(CurrentInfo->GetData(), *GetActivePreset()); Tasks.Add(Task); return Task; } -void USaveManager::FinishTask(USlotDataTask* Task) +void USaveManager::FinishTask(USaveSlotDataTask* Task) { Tasks.Remove(Task); @@ -359,26 +320,26 @@ void USaveManager::FinishTask(USlotDataTask* Task) FName USaveManager::GetSlotNameFromId(const int32 SlotId) const { - if (const auto* Preset = GetPreset()) + if (const auto* Preset = GetActivePreset()) { FName Name; Preset->BPGetSlotNameFromId(SlotId, Name); return Name; } - return FName{ FString::FromInt(SlotId) }; + return FName{FString::FromInt(SlotId)}; } bool USaveManager::IsLoading() const { return HasTasks() && - (Tasks[0]->IsA() || Tasks[0]->IsA()); + (Tasks[0]->IsA() || Tasks[0]->IsA()); } void USaveManager::Tick(float DeltaTime) { if (Tasks.Num()) { - USlotDataTask* Task = Tasks[0]; + USaveSlotDataTask* Task = Tasks[0]; check(Task); if (Task->IsRunning()) { @@ -403,8 +364,7 @@ void USaveManager::OnSaveBegan(const FSELevelFilter& Filter) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnSaveBegan); - IterateSubscribedInterfaces([&Filter](auto* Object) - { + IterateSubscribedInterfaces([&Filter](auto* Object) { check(Object->template Implements()); // C++ event @@ -420,8 +380,7 @@ void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bErro { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnSaveFinished); - IterateSubscribedInterfaces([&Filter, bError](auto* Object) - { + IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -442,8 +401,7 @@ void USaveManager::OnLoadBegan(const FSELevelFilter& Filter) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadBegan); - IterateSubscribedInterfaces([&Filter](auto* Object) - { + IterateSubscribedInterfaces([&Filter](auto* Object) { check(Object->template Implements()); // C++ event @@ -459,8 +417,7 @@ void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bErro { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadFinished); - IterateSubscribedInterfaces([&Filter, bError](auto* Object) - { + IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -479,12 +436,12 @@ void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bErro void USaveManager::OnMapLoadStarted(const FString& MapName) { - SELog(GetPreset(), "Loading Map '" + MapName + "'", FColor::Purple); + SELog(GetActivePreset(), "Loading Map '" + MapName + "'", FColor::Purple); } void USaveManager::OnMapLoadFinished(UWorld* LoadedWorld) { - if(auto* ActiveLoader = Cast(Tasks.Num() ? Tasks[0] : nullptr)) + if (auto* ActiveLoader = Cast(Tasks.Num() ? Tasks[0] : nullptr)) { ActiveLoader->OnMapLoaded(); } diff --git a/Source/SaveExtension/Private/SavePreset.cpp b/Source/SaveExtension/Private/SavePreset.cpp index a8d9665..2fbcd9c 100644 --- a/Source/SaveExtension/Private/SavePreset.cpp +++ b/Source/SaveExtension/Private/SavePreset.cpp @@ -1,32 +1,18 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SavePreset.h" #include "LevelFilter.h" -#include "SlotData.h" -#include "SlotInfo.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" -USavePreset::USavePreset() - : Super() - , SlotInfoClass(USlotInfo::StaticClass()) - , SlotDataClass(USlotData::StaticClass()) -{} - void USavePreset::BPGetSlotNameFromId_Implementation(int32 Id, FName& Name) const { // Call C++ inheritance chain by default return GetSlotNameFromId(Id, Name); } -void USavePreset::GetSlotNameFromId(int32 Id, FName& Name) const -{ - if (IsValidId(Id)) - { - Name = FName{ FString::FromInt(Id) }; - } -} - FSELevelFilter USavePreset::ToFilter() const { FSELevelFilter Filter{}; diff --git a/Source/SaveExtension/Private/SlotInfo.cpp b/Source/SaveExtension/Private/SaveSlot.cpp similarity index 62% rename from Source/SaveExtension/Private/SlotInfo.cpp rename to Source/SaveExtension/Private/SaveSlot.cpp index a15794c..33c653d 100644 --- a/Source/SaveExtension/Private/SlotInfo.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -1,17 +1,32 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. -#include "SlotInfo.h" +#include "SaveSlot.h" +#include +#include #include +#include #include #include -#include -#include #include -#include -UTexture2D* USlotInfo::GetThumbnail() const +USaveSlot::USaveSlot() +{ + Data = NewObject(this, DataClass); +} + +bool USaveSlot::OnSetId(int32 Id) +{ + FileName = FName{FString::FromInt(SlotId)}; +} + +int32 USaveSlot::OnGetId() const +{ + return FCString::Atoi(FileName.ToString()); +} + +UTexture2D* USaveSlot::GetThumbnail() const { if (ThumbnailPath.IsEmpty()) { @@ -24,18 +39,20 @@ UTexture2D* USlotInfo::GetThumbnail() const } // Load thumbnail as Texture2D - UTexture2D* Texture{ nullptr }; + UTexture2D* Texture{nullptr}; TArray RawFileData; if (GEngine && FFileHelper::LoadFileToArray(RawFileData, *ThumbnailPath)) { - IImageWrapperModule & ImageWrapperModule = FModuleManager::LoadModuleChecked < IImageWrapperModule >(FName("ImageWrapper")); + IImageWrapperModule& ImageWrapperModule = + FModuleManager::LoadModuleChecked(FName("ImageWrapper")); TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(RawFileData.GetData(), RawFileData.Num())) { TArray64 UncompressedBGRA; if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) { - Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); + Texture = UTexture2D::CreateTransient( + ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); @@ -43,11 +60,11 @@ UTexture2D* USlotInfo::GetThumbnail() const } } } - const_cast(this)->CachedThumbnail = Texture; + const_cast(this)->CachedThumbnail = Texture; return Texture; } -bool USlotInfo::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height /*= 360*/) +bool USaveSlot::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height /*= 360*/) { if (!GEngine || !GEngine->GameViewport || FileName.IsNone()) { @@ -67,9 +84,9 @@ bool USlotInfo::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig(); HighResScreenshotConfig.SetHDRCapture(false); - //Set Screenshot path + // Set Screenshot path HighResScreenshotConfig.FilenameOverride = ThumbnailPath; - //Set Screenshot Resolution + // Set Screenshot Resolution GScreenshotResolutionX = Width; GScreenshotResolutionY = Height; Viewport->TakeHighResScreenShot(); @@ -78,7 +95,18 @@ bool USlotInfo::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height return false; } -void USlotInfo::_SetThumbnailPath(const FString& Path) + +int32 USaveSlot::GetMaxIds() const +{ + return MaxSlots <= 0 ? 16384 : MaxSlots; +} + +bool USaveSlot::IsValidId(int32 CheckedId) +{ + return CheckedId >= 0 && CheckedId < GetMaxIds(); +} + +void USaveSlot::_SetThumbnailPath(const FString& Path) { if (ThumbnailPath != Path) { @@ -87,3 +115,12 @@ void USlotInfo::_SetThumbnailPath(const FString& Path) } } +bool USaveSlot::SetId_Implementation(int32 Id) +{ + return OnSetId(Id); +} + +int32 USaveSlot::GetId_Implementation() const +{ + return OnGetId(); +} \ No newline at end of file diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp new file mode 100644 index 0000000..f94ed8c --- /dev/null +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -0,0 +1,36 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "SaveSlotData.h" + +#include "SavePreset.h" + +#include + + +///////////////////////////////////////////////////// +// USaveSlotData + +void USaveSlotData::Serialize(FArchive& Ar) +{ + Super::Serialize(Ar); + + Ar << bStoreGameInstance; + Ar << GameInstance; + + static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; + LevelFilterType->SerializeItem(Ar, &GeneralLevelFilter, nullptr); + MainLevel.Serialize(Ar); + Ar << SubLevels; +} + +void USaveSlotData::CleanRecords(bool bKeepSublevels) +{ + // Clean Up serialization data + GameInstance = {}; + + MainLevel.CleanRecords(); + if (!bKeepSublevels) + { + SubLevels.Empty(); + } +} diff --git a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp index 99c7066..9ae45ee 100644 --- a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp +++ b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp @@ -1,13 +1,14 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/LevelRecords.h" -#include "SlotData.h" + +#include "SaveSlotData.h" ///////////////////////////////////////////////////// // LevelRecords -const FName FPersistentLevelRecord::PersistentName{ "Persistent" }; +const FName FPersistentLevelRecord::PersistentName{"Persistent"}; bool FLevelRecord::Serialize(FArchive& Ar) @@ -17,7 +18,7 @@ bool FLevelRecord::Serialize(FArchive& Ar) Ar << bOverrideGeneralFilter; if (bOverrideGeneralFilter) { - static UScriptStruct* const LevelFilterType{ FSELevelFilter::StaticStruct() }; + static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; LevelFilterType->SerializeItem(Ar, &Filter, nullptr); } diff --git a/Source/SaveExtension/Private/Serialization/MTTask.cpp b/Source/SaveExtension/Private/Serialization/MTTask.cpp index cb4369d..6cc36f9 100644 --- a/Source/SaveExtension/Private/Serialization/MTTask.cpp +++ b/Source/SaveExtension/Private/Serialization/MTTask.cpp @@ -1,3 +1,3 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/MTTask.h" diff --git a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp index e2e8836..e84cc0e 100644 --- a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp +++ b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp @@ -1,15 +1,17 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/MTTask_SerializeActors.h" -#include -#include #include "SaveManager.h" -#include "SlotInfo.h" -#include "SlotData.h" #include "SavePreset.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" #include "Serialization/SEArchive.h" +#include +#include + + ///////////////////////////////////////////////////// // FMTTask_SerializeActors @@ -37,9 +39,9 @@ void FMTTask_SerializeActors::SerializeGameInstance() TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeGameInstance); if (UGameInstance* GameInstance = World->GetGameInstance()) { - FObjectRecord Record{ GameInstance }; + FObjectRecord Record{GameInstance}; - //Serialize into Record Data + // Serialize into Record Data FMemoryWriter MemoryWriter(Record.Data, true); FSEArchive Archive(MemoryWriter, false); GameInstance->Serialize(Archive); @@ -52,8 +54,8 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& { TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActor); - //Clean the record - Record = { Actor }; + // Clean the record + Record = {Actor}; Record.bHiddenInGame = Actor->IsHidden(); Record.bIsProcedural = Filter.IsProcedural(Actor); @@ -109,14 +111,15 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& return true; } -void FMTTask_SerializeActors::SerializeActorComponents(const AActor* Actor, FActorRecord& ActorRecord, int8 Indent /*= 0*/) const +void FMTTask_SerializeActors::SerializeActorComponents( + const AActor* Actor, FActorRecord& ActorRecord, int8 Indent /*= 0*/) const { TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents); const TSet& Components = Actor->GetComponents(); for (auto* Component : Components) { - TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents|Component); + TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents | Component); if (Filter.ShouldSave(Component)) { FComponentRecord ComponentRecord; diff --git a/Source/SaveExtension/Private/Serialization/Records.cpp b/Source/SaveExtension/Private/Serialization/Records.cpp index 1faf389..797d56e 100644 --- a/Source/SaveExtension/Private/Serialization/Records.cpp +++ b/Source/SaveExtension/Private/Serialization/Records.cpp @@ -1,7 +1,8 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/Records.h" -#include "SlotData.h" + +#include "SaveSlotData.h" ///////////////////////////////////////////////////// @@ -66,7 +67,7 @@ bool FActorRecord::Serialize(FArchive& Ar) // Reduce memory footprint to 1 bool if not moving bool bIsMoving = Ar.IsSaving() && (!LinearVelocity.IsNearlyZero() || !AngularVelocity.IsNearlyZero()); Ar << bIsMoving; - if(bIsMoving) + if (bIsMoving) { Ar << LinearVelocity; Ar << AngularVelocity; diff --git a/Source/SaveExtension/Private/Serialization/SEArchive.cpp b/Source/SaveExtension/Private/Serialization/SEArchive.cpp index 4255783..71eb8dc 100644 --- a/Source/SaveExtension/Private/Serialization/SEArchive.cpp +++ b/Source/SaveExtension/Private/Serialization/SEArchive.cpp @@ -1,9 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/SEArchive.h" + #include + ///////////////////////////////////////////////////// // FSEArchive @@ -45,7 +47,7 @@ FArchive& FSEArchive::operator<<(UObject*& Obj) if (Obj) { // Serialize the fully qualified object name - FString SavedString{ Obj->GetPathName() }; + FString SavedString{Obj->GetPathName()}; InnerArchive << SavedString; /*bool bIsLocallyOwned = IsObjectOwned(Obj); @@ -57,7 +59,7 @@ FArchive& FSEArchive::operator<<(UObject*& Obj) } else { - FString SavedString{ "" }; + FString SavedString{""}; InnerArchive << SavedString; /*bool bIsLocallyOwned = false; diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp index a5cd2de..1f84b0b 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/SlotDataTask.h" @@ -9,7 +9,7 @@ ///////////////////////////////////////////////////// // USaveDataTask -USlotDataTask* USlotDataTask::Start() +USaveSlotDataTask* USaveSlotDataTask::Start() { const USaveManager* Manager = GetManager(); @@ -22,7 +22,7 @@ USlotDataTask* USlotDataTask::Start() return this; } -void USlotDataTask::Finish(bool bSuccess) +void USaveSlotDataTask::Finish(bool bSuccess) { if (bRunning) { @@ -34,59 +34,59 @@ void USlotDataTask::Finish(bool bSuccess) } } -bool USlotDataTask::IsScheduled() const +bool USaveSlotDataTask::IsScheduled() const { return GetManager()->Tasks.Contains(this); } -USaveManager* USlotDataTask::GetManager() const +USaveManager* USaveSlotDataTask::GetManager() const { return Cast(GetOuter()); } -void USlotDataTask::BakeAllFilters() +void USaveSlotDataTask::BakeAllFilters() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask::BakeAllFilters); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask::BakeAllFilters); SlotData->GeneralLevelFilter.BakeAllowedClasses(); - if(SlotData->MainLevel.bOverrideGeneralFilter) + if (SlotData->MainLevel.bOverrideGeneralFilter) { SlotData->MainLevel.Filter.BakeAllowedClasses(); } - for(const auto& Level : SlotData->SubLevels) + for (const auto& Level : SlotData->SubLevels) { - if(Level.bOverrideGeneralFilter) + if (Level.bOverrideGeneralFilter) { Level.Filter.BakeAllowedClasses(); } } } -const FSELevelFilter& USlotDataTask::GetGeneralFilter() const +const FSELevelFilter& USaveSlotDataTask::GetGeneralFilter() const { check(SlotData); return SlotData->GeneralLevelFilter; } -const FSELevelFilter& USlotDataTask::GetLevelFilter(const FLevelRecord& Level) const +const FSELevelFilter& USaveSlotDataTask::GetLevelFilter(const FLevelRecord& Level) const { - if(Level.bOverrideGeneralFilter) + if (Level.bOverrideGeneralFilter) { return Level.Filter; } return GetGeneralFilter(); } -FLevelRecord* USlotDataTask::FindLevelRecord(const ULevelStreaming* Level) const +FLevelRecord* USaveSlotDataTask::FindLevelRecord(const ULevelStreaming* Level) const { if (!Level) return &SlotData->MainLevel; - else // Find the Sub-Level + else // Find the Sub-Level return SlotData->SubLevels.FindByKey(Level); } -UWorld* USlotDataTask::GetWorld() const +UWorld* USaveSlotDataTask::GetWorld() const { return GetOuter()->GetWorld(); } diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp index 362e2de..d9c87fc 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/SlotDataTask_LevelLoader.h" @@ -6,7 +6,7 @@ ///////////////////////////////////////////////////// // USaveDataTask_LevelLoader -void USlotDataTask_LevelLoader::OnStart() +void USaveSlotDataTask_LevelLoader::OnStart() { if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) { @@ -33,7 +33,7 @@ void USlotDataTask_LevelLoader::OnStart() Finish(false); } -void USlotDataTask_LevelLoader::DeserializeASyncLoop(float StartMS /*= 0.0f*/) +void USaveSlotDataTask_LevelLoader::DeserializeASyncLoop(float StartMS /*= 0.0f*/) { FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); @@ -47,7 +47,7 @@ void USlotDataTask_LevelLoader::DeserializeASyncLoop(float StartMS /*= 0.0f*/) // Continue Iterating actors every tick for (; CurrentActorIndex < CurrentLevelActors.Num(); ++CurrentActorIndex) { - AActor* Actor{ CurrentLevelActors[CurrentActorIndex].Get() }; + AActor* Actor{CurrentLevelActors[CurrentActorIndex].Get()}; if (Actor) { DeserializeLevel_Actor(Actor, LevelRecord, Filter); diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp index 72eae1c..cd1bda8 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/SlotDataTask_LevelSaver.h" @@ -6,7 +6,7 @@ ///////////////////////////////////////////////////// // FSaveDataTask_LevelSaver -void USlotDataTask_LevelSaver::OnStart() +void USaveSlotDataTask_LevelSaver::OnStart() { if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) { diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp index 720a229..814d05a 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp @@ -1,33 +1,33 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/SlotDataTask_Loader.h" -#include -#include -#include -#include -#include - #include "Misc/SlotHelpers.h" -#include "SavePreset.h" #include "SaveManager.h" +#include "SavePreset.h" #include "Serialization/SEArchive.h" +#include +#include +#include +#include +#include + ///////////////////////////////////////////////////// // Helpers namespace Loader { - static int32 RemoveSingleRecordPtrSwap(TArray& Records, AActor* Actor, bool bAllowShrinking = true) + static int32 RemoveSingleRecordPtrSwap( + TArray& Records, AActor* Actor, bool bAllowShrinking = true) { - if(!Actor) + if (!Actor) { return 0; } - const int32 I = Records.IndexOfByPredicate([Records, Actor](auto* Record) - { + const int32 I = Records.IndexOfByPredicate([Records, Actor](auto* Record) { return *Record == Actor; }); if (I != INDEX_NONE) @@ -37,15 +37,15 @@ namespace Loader } return 0; } -} +} // namespace Loader ///////////////////////////////////////////////////// // USaveDataTask_Loader -void USlotDataTask_Loader::OnStart() +void USaveSlotDataTask_Loader::OnStart() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::OnStart); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnStart); USaveManager* Manager = GetManager(); SELog(Preset, "Loading from Slot " + SlotName.ToString()); @@ -65,21 +65,26 @@ void USlotDataTask_Loader::OnStart() // Cross-Level loading // TODO: Handle empty Map as empty world - FName CurrentMapName { FSlotHelpers::GetWorldName(World) }; + FName CurrentMapName{FSlotHelpers::GetWorldName(World)}; if (CurrentMapName != NewSlotInfo->Map) { LoadState = ELoadDataTaskState::LoadingMap; FString MapToOpen = NewSlotInfo->Map.ToString(); if (!GEngine->MakeSureMapNameIsValid(MapToOpen)) { - UE_LOG(LogSaveExtension, Warning, TEXT("Slot '%s' was saved in map '%s' but it did not exist while loading. Corrupted save file?"), *NewSlotInfo->FileName.ToString(), *MapToOpen); + UE_LOG(LogSaveExtension, Warning, + TEXT("Slot '%s' was saved in map '%s' but it did not exist while loading. Corrupted save " + "file?"), + *NewSlotInfo->FileName.ToString(), *MapToOpen); Finish(false); return; } - UGameplayStatics::OpenLevel(this, FName{ MapToOpen }); + UGameplayStatics::OpenLevel(this, FName{MapToOpen}); - SELog(Preset, "Slot '" + SlotName.ToString() + "' is recorded on another Map. Loading before charging slot.", FColor::White, false, 1); + SELog(Preset, + "Slot '" + SlotName.ToString() + "' is recorded on another Map. Loading before charging slot.", + FColor::White, false, 1); return; } else if (IsDataLoaded()) @@ -92,29 +97,29 @@ void USlotDataTask_Loader::OnStart() } } -void USlotDataTask_Loader::Tick(float DeltaTime) +void USaveSlotDataTask_Loader::Tick(float DeltaTime) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::Tick); - switch(LoadState) + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::Tick); + switch (LoadState) { - case ELoadDataTaskState::Deserializing: - if (CurrentLevel.IsValid()) - { - DeserializeASyncLoop(); - } - break; + case ELoadDataTaskState::Deserializing: + if (CurrentLevel.IsValid()) + { + DeserializeASyncLoop(); + } + break; - case ELoadDataTaskState::WaitingForData: - if (IsDataLoaded()) - { - StartDeserialization(); - } + case ELoadDataTaskState::WaitingForData: + if (IsDataLoaded()) + { + StartDeserialization(); + } } } -void USlotDataTask_Loader::OnFinish(bool bSuccess) +void USaveSlotDataTask_Loader::OnFinish(bool bSuccess) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::OnFinish); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnFinish); if (bSuccess) { SELog(Preset, "Finished Loading", FColor::Green); @@ -123,13 +128,10 @@ void USlotDataTask_Loader::OnFinish(bool bSuccess) // Execute delegates Delegate.ExecuteIfBound((bSuccess) ? NewSlotInfo : nullptr); - GetManager()->OnLoadFinished( - SlotData? GetGeneralFilter() : FSELevelFilter{}, - !bSuccess - ); + GetManager()->OnLoadFinished(SlotData ? GetGeneralFilter() : FSELevelFilter{}, !bSuccess); } -void USlotDataTask_Loader::BeginDestroy() +void USaveSlotDataTask_Loader::BeginDestroy() { if (LoadDataTask) { @@ -140,23 +142,23 @@ void USlotDataTask_Loader::BeginDestroy() Super::BeginDestroy(); } -void USlotDataTask_Loader::OnMapLoaded() +void USaveSlotDataTask_Loader::OnMapLoaded() { - if(LoadState != ELoadDataTaskState::LoadingMap) + if (LoadState != ELoadDataTaskState::LoadingMap) { return; } const UWorld* World = GetWorld(); - if(!World) + if (!World) { UE_LOG(LogSaveExtension, Warning, TEXT("Failed loading map from saved slot.")); Finish(false); } - const FName NewMapName { FSlotHelpers::GetWorldName(World) }; + const FName NewMapName{FSlotHelpers::GetWorldName(World)}; if (NewMapName == NewSlotInfo->Map) { - if(IsDataLoaded()) + if (IsDataLoaded()) { StartDeserialization(); } @@ -167,9 +169,9 @@ void USlotDataTask_Loader::OnMapLoaded() } } -void USlotDataTask_Loader::StartDeserialization() +void USaveSlotDataTask_Loader::StartDeserialization() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::StartDeserialization); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::StartDeserialization); check(NewSlotInfo); LoadState = ELoadDataTaskState::Deserializing; @@ -185,7 +187,7 @@ void USlotDataTask_Loader::StartDeserialization() NewSlotInfo->LoadDate = FDateTime::Now(); GetManager()->OnLoadBegan(GetGeneralFilter()); - //Apply current Info if succeeded + // Apply current Info if succeeded GetManager()->__SetCurrentInfo(NewSlotInfo); BakeAllFilters(); @@ -198,7 +200,7 @@ void USlotDataTask_Loader::StartDeserialization() DeserializeSync(); } -void USlotDataTask_Loader::StartLoadingData() +void USaveSlotDataTask_Loader::StartLoadingData() { LoadDataTask = new FAsyncTask(GetManager(), SlotName.ToString()); @@ -208,7 +210,7 @@ void USlotDataTask_Loader::StartLoadingData() LoadDataTask->StartSynchronousTask(); } -USlotData* USlotDataTask_Loader::GetLoadedData() const +USaveSlotData* USaveSlotDataTask_Loader::GetLoadedData() const { if (IsDataLoaded()) { @@ -217,9 +219,9 @@ USlotData* USlotDataTask_Loader::GetLoadedData() const return nullptr; } -void USlotDataTask_Loader::BeforeDeserialize() +void USaveSlotDataTask_Loader::BeforeDeserialize() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::BeforeDeserialize); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::BeforeDeserialize); UWorld* World = GetWorld(); // Set current game time to the saved value @@ -231,9 +233,9 @@ void USlotDataTask_Loader::BeforeDeserialize() } } -void USlotDataTask_Loader::DeserializeSync() +void USaveSlotDataTask_Loader::DeserializeSync() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::DeserializeSync); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeSync); const UWorld* World = GetWorld(); check(World); @@ -259,14 +261,16 @@ void USlotDataTask_Loader::DeserializeSync() FinishedDeserializing(); } -void USlotDataTask_Loader::DeserializeLevelSync(const ULevel* Level, const ULevelStreaming* StreamingLevel) +void USaveSlotDataTask_Loader::DeserializeLevelSync( + const ULevel* Level, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::DeserializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeLevelSync); if (!IsValid(Level)) return; - const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; + const FName LevelName = + StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Preset, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); if (FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel)) @@ -284,7 +288,7 @@ void USlotDataTask_Loader::DeserializeLevelSync(const ULevel* Level, const ULeve } } -void USlotDataTask_Loader::DeserializeASync() +void USaveSlotDataTask_Loader::DeserializeASync() { // Deserialize world { @@ -295,15 +299,17 @@ void USlotDataTask_Loader::DeserializeASync() } } -void USlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStreaming* StreamingLevel) +void USaveSlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStreaming* StreamingLevel) { check(IsValid(Level)); - const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; + const FName LevelName = + StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Preset, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); - if (!LevelRecord) { + if (!LevelRecord) + { Finish(false); return; } @@ -318,7 +324,7 @@ void USlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStreaming* CurrentLevelActors.Empty(Level->Actors.Num()); for (auto* Actor : Level->Actors) { - if(IsValid(Actor)) + if (IsValid(Actor)) { CurrentLevelActors.Add(Actor); } @@ -327,9 +333,9 @@ void USlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStreaming* DeserializeASyncLoop(StartMS); } -void USlotDataTask_Loader::DeserializeASyncLoop(float StartMS) +void USaveSlotDataTask_Loader::DeserializeASyncLoop(float StartMS) { - FLevelRecord * LevelRecord = FindLevelRecord(CurrentSLevel.Get()); + FLevelRecord* LevelRecord = FindLevelRecord(CurrentSLevel.Get()); if (!LevelRecord) { return; @@ -337,7 +343,7 @@ void USlotDataTask_Loader::DeserializeASyncLoop(float StartMS) const auto& Filter = GetLevelFilter(*LevelRecord); - if(StartMS <= 0) + if (StartMS <= 0) { StartMS = GetTimeMilliseconds(); } @@ -345,7 +351,7 @@ void USlotDataTask_Loader::DeserializeASyncLoop(float StartMS) // Continue Iterating actors every tick for (; CurrentActorIndex < CurrentLevelActors.Num(); ++CurrentActorIndex) { - AActor* const Actor{ CurrentLevelActors[CurrentActorIndex].Get() }; + AActor* const Actor{CurrentLevelActors[CurrentActorIndex].Get()}; if (IsValid(Actor) && Filter.ShouldSave(Actor)) { DeserializeLevel_Actor(Actor, *LevelRecord, Filter); @@ -376,9 +382,9 @@ void USlotDataTask_Loader::DeserializeASyncLoop(float StartMS) FinishedDeserializing(); } -void USlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) +void USaveSlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::PrepareLevel); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::PrepareLevel); const auto& Filter = GetLevelFilter(LevelRecord); @@ -389,7 +395,7 @@ void USlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& Level TArray ActorsToSpawn; ActorsToSpawn.Reserve(LevelRecord.Actors.Num()); - for(FActorRecord& Record : LevelRecord.Actors) + for (FActorRecord& Record : LevelRecord.Actors) { ActorsToSpawn.Add(&Record); } @@ -404,7 +410,7 @@ void USlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& Level if (Actor && Filter.ShouldSave(Actor)) { - if (!bFoundActorRecord) // Don't destroy level actors + if (!bFoundActorRecord) // Don't destroy level actors { // If the actor wasn't found, mark it for destruction Actor->Destroy(); @@ -418,7 +424,7 @@ void USlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& Level RespawnActors(ActorsToSpawn, Level); } -void USlotDataTask_Loader::FinishedDeserializing() +void USaveSlotDataTask_Loader::FinishedDeserializing() { // Clean serialization data SlotData->CleanRecords(true); @@ -427,9 +433,9 @@ void USlotDataTask_Loader::FinishedDeserializing() Finish(true); } -void USlotDataTask_Loader::PrepareAllLevels() +void USaveSlotDataTask_Loader::PrepareAllLevels() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::PrepareAllLevels); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::PrepareAllLevels); const UWorld* World = GetWorld(); check(World); @@ -452,9 +458,9 @@ void USlotDataTask_Loader::PrepareAllLevels() } } -void USlotDataTask_Loader::RespawnActors(const TArray& Records, const ULevel* Level) +void USaveSlotDataTask_Loader::RespawnActors(const TArray& Records, const ULevel* Level) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::RespawnActors); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::RespawnActors); FActorSpawnParameters SpawnInfo{}; SpawnInfo.OverrideLevel = const_cast(Level); @@ -473,9 +479,10 @@ void USlotDataTask_Loader::RespawnActors(const TArray& Records, c } } -void USlotDataTask_Loader::DeserializeLevel_Actor(AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter) +void USaveSlotDataTask_Loader::DeserializeLevel_Actor( + AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::DeserializeLevel_Actor); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeLevel_Actor); // Find the record const FActorRecord* const Record = LevelRecord.Actors.FindByKey(Actor); @@ -485,7 +492,7 @@ void USlotDataTask_Loader::DeserializeLevel_Actor(AActor* const Actor, const FLe } } -void USlotDataTask_Loader::DeserializeGameInstance() +void USaveSlotDataTask_Loader::DeserializeGameInstance() { bool bSuccess = true; auto* GameInstance = GetWorld()->GetGameInstance(); @@ -496,7 +503,7 @@ void USlotDataTask_Loader::DeserializeGameInstance() if (bSuccess) { - //Serialize from Record Data + // Serialize from Record Data FMemoryReader MemoryReader(Record.Data, true); FSEArchive Archive(MemoryReader, false); GameInstance->Serialize(Archive); @@ -505,9 +512,10 @@ void USlotDataTask_Loader::DeserializeGameInstance() SELog(Preset, "Game Instance '" + Record.Name.ToString() + "'", FColor::Green, !bSuccess, 1); } -bool USlotDataTask_Loader::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter) +bool USaveSlotDataTask_Loader::DeserializeActor( + AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Loader::DeserializeActor); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeActor); // Always load saved tags Actor->Tags = Record.Tags; @@ -537,7 +545,7 @@ bool USlotDataTask_Loader::DeserializeActor(AActor* Actor, const FActorRecord& R DeserializeActorComponents(Actor, Record, Filter, 2); { - //Serialize from Record Data + // Serialize from Record Data FMemoryReader MemoryReader(Record.Data, true); FSEArchive Archive(MemoryReader, false); Actor->Serialize(Archive); @@ -546,11 +554,12 @@ bool USlotDataTask_Loader::DeserializeActor(AActor* Actor, const FActorRecord& R return true; } -void USlotDataTask_Loader::DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 Indent) +void USaveSlotDataTask_Loader::DeserializeActorComponents( + AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 Indent) { if (Filter.bStoreComponents) { - TRACE_CPUPROFILER_EVENT_SCOPE(UUSlotDataTask_Loader::DeserializeActorComponents); + TRACE_CPUPROFILER_EVENT_SCOPE(UUSaveSlotDataTask_Loader::DeserializeActorComponents); const TSet& Components = Actor->GetComponents(); for (auto* Component : Components) @@ -564,7 +573,8 @@ void USlotDataTask_Loader::DeserializeActorComponents(AActor* Actor, const FActo const FComponentRecord* Record = ActorRecord.ComponentRecords.FindByKey(Component); if (!Record) { - SELog(Preset, "Component '" + Component->GetFName().ToString() + "' - Record not found", FColor::Red, false, Indent + 1); + SELog(Preset, "Component '" + Component->GetFName().ToString() + "' - Record not found", + FColor::Red, false, Indent + 1); continue; } @@ -592,7 +602,7 @@ void USlotDataTask_Loader::DeserializeActorComponents(AActor* Actor, const FActo } } -void USlotDataTask_Loader::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const +void USaveSlotDataTask_Loader::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const { OutLevelStreaming = nullptr; @@ -602,7 +612,7 @@ void USlotDataTask_Loader::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreamin { if (!CurrentSLevel.IsValid()) { - //Current is persistent, get first streaming level + // Current is persistent, get first streaming level OutLevelStreaming = Levels[0]; return; } diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp index c67418b..6a81d3b 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp @@ -1,32 +1,32 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Serialization/SlotDataTask_Saver.h" -#include -#include - +#include "FileAdapter.h" #include "Misc/SlotHelpers.h" #include "SaveManager.h" -#include "SlotInfo.h" -#include "SlotData.h" #include "SavePreset.h" -#include "FileAdapter.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" + +#include +#include ///////////////////////////////////////////////////// // USaveDataTask_Saver -void USlotDataTask_Saver::OnStart() +void USaveSlotDataTask_Saver::OnStart() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::OnStart); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnStart); USaveManager* Manager = GetManager(); Manager->TryInstantiateInfo(); bool bSave = true; const FString SlotNameStr = SlotName.ToString(); - //Overriding + // Overriding { - const bool bFileExists = FFileAdapter::DoesFileExist(SlotNameStr); + const bool bFileExists = FFileAdapter::FileExists(SlotNameStr); if (bOverride) { // Delete previous save @@ -37,8 +37,8 @@ void USlotDataTask_Saver::OnStart() } else { - //Only save if previous files don't exist - //We don't want to serialize since it won't be saved anyway + // Only save if previous files don't exist + // We don't want to serialize since it won't be saved anyway bSave = !bFileExists; } } @@ -55,7 +55,7 @@ void USlotDataTask_Saver::OnStart() check(SlotInfo && SlotData); - const bool bSlotWasDifferent = SlotInfo->FileName != SlotName; + const bool bSlotExisted = SlotInfo->FileName == SlotName; SlotInfo->FileName = SlotName; if (bSaveThumbnail) @@ -66,34 +66,30 @@ void USlotDataTask_Saver::OnStart() // Time stats { SlotInfo->SaveDate = FDateTime::Now(); + FSaveSlotStats& Stats = SlotInfo->Stats; // If this info has been loaded ever - const bool bWasLoaded = SlotInfo->LoadDate.GetTicks() > 0; + const bool bWasLoaded = Stats.LoadDate.GetTicks() > 0; if (bWasLoaded) { // Now - Loaded - const FTimespan SessionTime = SlotInfo->SaveDate - SlotInfo->LoadDate; - - SlotInfo->PlayedTime += SessionTime; - - if (!bSlotWasDifferent) - SlotInfo->SlotPlayedTime += SessionTime; - else - SlotInfo->SlotPlayedTime = SessionTime; + const FTimespan SessionTime = Stats.SaveDate - Stats.LoadDate; + Stats.PlayedTime += SessionTime; + Stats.SlotPlayedTime = bSlotExisted ? (Stats.SlotPlayedTime + SessionTime) : SessionTime; } else { // Slot is new, played time is world seconds - SlotInfo->PlayedTime = FTimespan::FromSeconds(World->TimeSeconds); - SlotInfo->SlotPlayedTime = SlotInfo->PlayedTime; + Stats.PlayedTime = FTimespan::FromSeconds(World->TimeSeconds); + Stats.SlotPlayedTime = Stats.PlayedTime; } // Save current game seconds SlotData->TimeSeconds = World->TimeSeconds; } - //Save Level info in both files - SlotInfo->Map = FName{ FSlotHelpers::GetWorldName(World) }; + // Save Level info in both files + SlotInfo->Map = FName{FSlotHelpers::GetWorldName(World)}; SlotData->Map = SlotData->Map; SlotData->bStoreGameInstance = Preset->bStoreGameInstance; @@ -106,9 +102,9 @@ void USlotDataTask_Saver::OnStart() Finish(false); } -void USlotDataTask_Saver::Tick(float DeltaTime) +void USaveSlotDataTask_Saver::Tick(float DeltaTime) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::Tick); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::Tick); Super::Tick(DeltaTime); if (SaveTask && SaveTask->IsDone()) @@ -127,9 +123,9 @@ void USlotDataTask_Saver::Tick(float DeltaTime) } } -void USlotDataTask_Saver::OnFinish(bool bSuccess) +void USaveSlotDataTask_Saver::OnFinish(bool bSuccess) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::OnFinish); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnFinish); if (bSuccess) { // Clean serialization data @@ -141,14 +137,11 @@ void USlotDataTask_Saver::OnFinish(bool bSuccess) // Execute delegates USaveManager* Manager = GetManager(); check(Manager); - Delegate.ExecuteIfBound((Manager && bSuccess)? Manager->GetCurrentInfo() : nullptr); - Manager->OnSaveFinished( - SlotData? GetGeneralFilter() : FSELevelFilter{}, - !bSuccess - ); + Delegate.ExecuteIfBound((Manager && bSuccess) ? Manager->GetCurrentInfo() : nullptr); + Manager->OnSaveFinished(SlotData ? GetGeneralFilter() : FSELevelFilter{}, !bSuccess); } -void USlotDataTask_Saver::BeginDestroy() +void USaveSlotDataTask_Saver::BeginDestroy() { if (SaveTask) { @@ -159,9 +152,9 @@ void USlotDataTask_Saver::BeginDestroy() Super::BeginDestroy(); } -void USlotDataTask_Saver::SerializeWorld() +void USaveSlotDataTask_Saver::SerializeWorld() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::SerializeWorld); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeWorld); // Must have Authority if (!GetWorld()->GetAuthGameMode()) @@ -192,7 +185,7 @@ void USlotDataTask_Saver::SerializeWorld() RunScheduledTasks(); } -void USlotDataTask_Saver::PrepareAllLevels(const TArray& Levels) +void USaveSlotDataTask_Saver::PrepareAllLevels(const TArray& Levels) { BakeAllFilters(); @@ -201,14 +194,15 @@ void USlotDataTask_Saver::PrepareAllLevels(const TArray& Level { if (Level->IsLevelLoaded()) { - SlotData->SubLevels.AddUnique({ *Level }); + SlotData->SubLevels.AddUnique({*Level}); } } } -void USlotDataTask_Saver::SerializeLevelSync(const ULevel* Level, int32 AssignedTasks, const ULevelStreaming* StreamingLevel) +void USaveSlotDataTask_Saver::SerializeLevelSync( + const ULevel* Level, int32 AssignedTasks, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::SerializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeLevelSync); check(IsValid(Level)); if (!Preset->IsMTSerializationSave()) @@ -216,7 +210,8 @@ void USlotDataTask_Saver::SerializeLevelSync(const ULevel* Level, int32 Assigned AssignedTasks = 1; } - const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; + const FName LevelName = + StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Preset, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level @@ -247,19 +242,16 @@ void USlotDataTask_Saver::SerializeLevelSync(const ULevel* Level, int32 Assigned // First task saves the GameInstance bool bStoreGameInstance = Index <= 0 && SlotData->bStoreGameInstance; // Add new Task - Tasks.Emplace(FMTTask_SerializeActors - { - GetWorld(), SlotData, &Level->Actors, Index, NumToSerialize, - bStoreGameInstance, LevelRecord, Filter - }); + Tasks.Emplace(FMTTask_SerializeActors{GetWorld(), SlotData, &Level->Actors, Index, NumToSerialize, + bStoreGameInstance, LevelRecord, Filter}); Index += NumToSerialize; } } -void USlotDataTask_Saver::RunScheduledTasks() +void USaveSlotDataTask_Saver::RunScheduledTasks() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::RunScheduledTasks); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::RunScheduledTasks); // Start all serialization tasks if (Tasks.Num() > 0) { @@ -286,14 +278,13 @@ void USlotDataTask_Saver::RunScheduledTasks() Tasks.Empty(); } -void USlotDataTask_Saver::SaveFile() +void USaveSlotDataTask_Saver::SaveFile() { - TRACE_CPUPROFILER_EVENT_SCOPE(USlotDataTask_Saver::SaveFile); + TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SaveFile); USaveManager* Manager = GetManager(); - SaveTask = new FAsyncTask( - Manager->GetCurrentInfo(), Manager->GetCurrentData(), - SlotName.ToString(), Preset->bUseCompression); + SaveTask = + new FAsyncTask(Manager->GetActiveSlot(), SlotName.ToString(), Preset->bUseCompression); if (Preset->IsMTFilesSave()) { diff --git a/Source/SaveExtension/Private/SlotData.cpp b/Source/SaveExtension/Private/SlotData.cpp deleted file mode 100644 index 5454cef..0000000 --- a/Source/SaveExtension/Private/SlotData.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#include "SlotData.h" -#include - -#include "SavePreset.h" - - -/** - * A macro for tidying up accessing of private members, through the above code - * - * @param InClass The class being accessed (not a string, just the class, i.e. FStackTracker) - * @param InObj Pointer to an instance of the specified class - * @param MemberName Name of the member being accessed (again, not a string) - * @return The value of the member - */ -#define GET_PRIVATE(InClass, InObj, MemberName) (*InObj).*GetPrivate(InClass##MemberName##Accessor()) - -///////////////////////////////////////////////////// -// USlotData - -void USlotData::Serialize(FArchive& Ar) -{ - Super::Serialize(Ar); - - Ar << bStoreGameInstance; - Ar << GameInstance; - - static UScriptStruct* const LevelFilterType{ FSELevelFilter::StaticStruct() }; - LevelFilterType->SerializeItem(Ar, &GeneralLevelFilter, nullptr); - MainLevel.Serialize(Ar); - Ar << SubLevels; -} - -void USlotData::CleanRecords(bool bKeepSublevels) -{ - //Clean Up serialization data - GameInstance = {}; - - MainLevel.CleanRecords(); - if (!bKeepSublevels) - { - SubLevels.Empty(); - } -} diff --git a/Source/SaveExtension/Public/Delegates.h b/Source/SaveExtension/Public/Delegates.h index 5e1ddaa..fbb9b04 100644 --- a/Source/SaveExtension/Public/Delegates.h +++ b/Source/SaveExtension/Public/Delegates.h @@ -1,16 +1,16 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include "SlotInfo.h" +#include "SaveSlot.h" /** Called when game has been saved - * @param SlotInfo the saved slot. Null if save failed + * @param SaveSlot the saved slot. Null if save failed */ -DECLARE_DELEGATE_OneParam(FOnGameSaved, USlotInfo*); +DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); /** Called when game has been loaded - * @param SlotInfo the loaded slot. Null if load failed + * @param SaveSlot the loaded slot. Null if load failed */ -DECLARE_DELEGATE_OneParam(FOnGameLoaded, USlotInfo*); +DECLARE_DELEGATE_OneParam(FOnGameLoaded, USaveSlot*); diff --git a/Source/SaveExtension/Public/FileAdapter.h b/Source/SaveExtension/Public/FileAdapter.h index 238cb5c..a0c9a2d 100644 --- a/Source/SaveExtension/Public/FileAdapter.h +++ b/Source/SaveExtension/Public/FileAdapter.h @@ -1,22 +1,22 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include +#include "ISaveExtension.h" + #include +#include #include #include -#include +#include #include #include -#include - -#include "ISaveExtension.h" +#include class USavePreset; -class USlotInfo; -class USlotData; +class USaveSlot; +class USaveSlotData; class FMemoryReader; class FMemoryWriter; @@ -33,9 +33,18 @@ struct FScopedFileWriter delete Writer; } - FArchive& GetArchive() { return *Writer; } - bool IsValid() const { return Writer != nullptr; } - bool IsError() const { return Writer && (Writer->IsError() || Writer->IsCriticalError()); } + FArchive& GetArchive() + { + return *Writer; + } + bool IsValid() const + { + return Writer != nullptr; + } + bool IsError() const + { + return Writer && (Writer->IsError() || Writer->IsCriticalError()); + } }; @@ -52,8 +61,14 @@ struct FScopedFileReader delete Reader; } - FArchive& GetArchive() { return *Reader; } - bool IsValid() const { return Reader != nullptr; } + FArchive& GetArchive() + { + return *Reader; + } + bool IsValid() const + { + return Reader != nullptr; + } }; @@ -62,7 +77,7 @@ struct FSaveFile { int32 FileTypeTag = 0; int32 SaveGameFileVersion = 0; - FPackageFileVersion PackageFileUEVersion {}; + FPackageFileVersion PackageFileUEVersion{}; FEngineVersion SavedEngineVersion; int32 CustomVersionFormat = int32(ECustomVersionSerializationFormat::Unknown); FCustomVersionContainer CustomVersions; @@ -83,10 +98,10 @@ struct FSaveFile void Read(FScopedFileReader& Reader, bool bSkipData); void Write(FScopedFileWriter& Writer, bool bCompressData); - void SerializeInfo(USlotInfo* SlotInfo); - void SerializeData(USlotData* SlotData); - USlotInfo* CreateAndDeserializeInfo(const UObject* Outer) const; - USlotData* CreateAndDeserializeData(const UObject* Outer) const; + void SerializeInfo(USaveSlot* SlotInfo); + void SerializeData(USaveSlotData* SlotData); + USaveSlot* CreateAndDeserializeInfo(const UObject* Outer) const; + void CreateAndDeserializeData(USaveSlot* Slot) const; }; @@ -94,11 +109,10 @@ struct FSaveFile class SAVEEXTENSION_API FFileAdapter { public: - - static bool SaveFile(FStringView SlotName, USlotInfo* Info, USlotData* Data, const bool bUseCompression); + static bool SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression); // Not safe for Multi-threading - static bool LoadFile(FStringView SlotName, USlotInfo*& Info, USlotData*& Data, bool bLoadData, const UObject* Outer); + static USaveSlot* LoadFile(FStringView SlotName, bool bLoadData, const UObject* Outer); static bool DeleteFile(FStringView SlotName); static bool DoesFileExist(FStringView SlotName); @@ -107,5 +121,6 @@ class SAVEEXTENSION_API FFileAdapter static FString GetSlotPath(FStringView SlotName); static FString GetThumbnailPath(FStringView SlotName); - static void DeserializeObject(UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes); + static void DeserializeObject( + UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes); }; diff --git a/Source/SaveExtension/Public/ISaveExtension.h b/Source/SaveExtension/Public/ISaveExtension.h index 2cfd2ae..5230ae8 100644 --- a/Source/SaveExtension/Public/ISaveExtension.h +++ b/Source/SaveExtension/Public/ISaveExtension.h @@ -1,21 +1,24 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include "Modules/ModuleManager.h" #include "Engine/Engine.h" - +#include "Modules/ModuleManager.h" #include "SavePreset.h" + DECLARE_LOG_CATEGORY_EXTERN(LogSaveExtension, All, All); -class ISaveExtension : public IModuleInterface { +class ISaveExtension : public IModuleInterface +{ public: - static inline ISaveExtension& Get() { + static inline ISaveExtension& Get() + { return FModuleManager::LoadModuleChecked("SaveExtension"); } - static inline bool IsAvailable() { + static inline bool IsAvailable() + { return FModuleManager::Get().IsModuleLoaded("SaveExtension"); } @@ -24,15 +27,17 @@ class ISaveExtension : public IModuleInterface { Log(Preset, Message, FColor::White, bError, 2.f); } - static void Log(const USavePreset* Preset, const FString Message, FColor Color = FColor::White, bool bError = false, const float Duration = 2.f) + static void Log(const USavePreset* Preset, const FString Message, FColor Color = FColor::White, + bool bError = false, const float Duration = 2.f) { if (Preset->bDebug) { - if (bError) { + if (bError) + { Color = FColor::Red; } - const FString ComposedMessage { FString::Printf(TEXT("SE: %s"), *Message) }; + const FString ComposedMessage{FString::Printf(TEXT("SE: %s"), *Message)}; if (bError) { @@ -51,11 +56,15 @@ class ISaveExtension : public IModuleInterface { } }; -//Only log in Editor +// Only log in Editor #if WITH_EDITORONLY_DATA -template -void SELog(Args&& ...args) { ISaveExtension::Log(Forward(args)...); } +template +void SELog(Args&&... args) +{ + ISaveExtension::Log(Forward(args)...); +} #else -template -void SELog(Args&& ...args) {} // Optimized away by compiler +template +void SELog(Args&&... args) +{} // Optimized away by compiler #endif diff --git a/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h b/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h index fea0a52..a1c6ffd 100644 --- a/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h +++ b/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -8,7 +8,7 @@ class USaveManager; -class USlotInfo; +class USaveSlot; /** * Enum used to indicate quote execution results @@ -35,7 +35,8 @@ class FDeleteSlotsAction : public FPendingLatentAction /** * @param SlotId will load that Saved Game if Id > 0, otherwise it will load all infos */ - FDeleteSlotsAction(USaveManager* Manager, EDeleteSlotsResult& OutResult, const FLatentActionInfo& LatentInfo); + FDeleteSlotsAction( + USaveManager* Manager, EDeleteSlotsResult& OutResult, const FLatentActionInfo& LatentInfo); virtual void UpdateOperation(FLatentResponse& Response) override; diff --git a/Source/SaveExtension/Public/LatentActions/LoadGameAction.h b/Source/SaveExtension/Public/LatentActions/LoadGameAction.h index 444d721..cfa1977 100644 --- a/Source/SaveExtension/Public/LatentActions/LoadGameAction.h +++ b/Source/SaveExtension/Public/LatentActions/LoadGameAction.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -8,7 +8,7 @@ class USaveManager; -class USlotInfo; +class USaveSlot; /** * Enum used to indicate quote execution results @@ -32,11 +32,12 @@ class FLoadGameAction : public FPendingLatentAction FWeakObjectPtr CallbackTarget; - FLoadGameAction(USaveManager* Manager, FName SlotName, ELoadGameResult& Result, const FLatentActionInfo& LatentInfo); + FLoadGameAction( + USaveManager* Manager, FName SlotName, ELoadGameResult& Result, const FLatentActionInfo& LatentInfo); virtual void UpdateOperation(FLatentResponse& Response) override; - void OnLoadFinished(USlotInfo* SavedSlot); + void OnLoadFinished(USaveSlot* SavedSlot); #if WITH_EDITOR // Returns a human readable description of the latent operation's current state diff --git a/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h b/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h index 34df5e8..1e624b7 100644 --- a/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h +++ b/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h @@ -1,16 +1,16 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "Multithreading/LoadSlotInfosTask.h" + #include #include #include -#include "Multithreading/LoadSlotInfosTask.h" - class USaveManager; -class USlotInfo; +class USaveSlot; /** * Enum used to indicate quote execution results @@ -24,11 +24,10 @@ enum class ELoadInfoResult : uint8 /** FLoadInfosction */ class FLoadInfosAction : public FPendingLatentAction { - public: ELoadInfoResult& Result; - TArray& SlotInfos; + TArray& SlotInfos; bool bFinished; FName ExecutionFunction; @@ -39,7 +38,8 @@ class FLoadInfosAction : public FPendingLatentAction /** * @param SlotId will load that Saved Game if Id > 0, otherwise it will load all infos */ - FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, TArray& SaveInfos, ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo); + FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, TArray& SaveInfos, + ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo); virtual void UpdateOperation(FLatentResponse& Response) override; diff --git a/Source/SaveExtension/Public/LatentActions/SaveGameAction.h b/Source/SaveExtension/Public/LatentActions/SaveGameAction.h index 7c669c7..e246756 100644 --- a/Source/SaveExtension/Public/LatentActions/SaveGameAction.h +++ b/Source/SaveExtension/Public/LatentActions/SaveGameAction.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -8,7 +8,7 @@ class USaveManager; -class USlotInfo; +class USaveSlot; struct FScreenshotSize; /** @@ -25,7 +25,6 @@ enum class ESaveGameResult : uint8 /** FSaveGameAction */ class FSaveGameAction : public FPendingLatentAction { - public: ESaveGameResult& Result; @@ -34,11 +33,12 @@ class FSaveGameAction : public FPendingLatentAction FWeakObjectPtr CallbackTarget; - FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& OutResult, const FLatentActionInfo& LatentInfo); + FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, + const FScreenshotSize Size, ESaveGameResult& OutResult, const FLatentActionInfo& LatentInfo); virtual void UpdateOperation(FLatentResponse& Response) override; - void OnSaveFinished(USlotInfo* SavedSlot); + void OnSaveFinished(USaveSlot* SavedSlot); #if WITH_EDITOR // Returns a human readable description of the latent operation's current state diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 6bf7d72..2b55c68 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -1,10 +1,12 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "SavePreset.h" + #include "LevelFilter.generated.h" + class USaveManager; @@ -23,7 +25,6 @@ struct FSELevelFilter static const FName TagTransform; public: - UPROPERTY(SaveGame) FSEActorClassFilter ActorFilter; @@ -72,20 +73,17 @@ struct FSELevelFilter bool ShouldSave(const UActorComponent* Component) const { - return IsValid(Component) - && ComponentFilter.IsClassAllowed(Component->GetClass()); + return IsValid(Component) && ComponentFilter.IsClassAllowed(Component->GetClass()); } bool ShouldLoad(const UActorComponent* Component) const { - return IsValid(Component) - && LoadComponentFilter.IsClassAllowed(Component->GetClass()); + return IsValid(Component) && LoadComponentFilter.IsClassAllowed(Component->GetClass()); } static bool StoresTransform(const UActorComponent* Component) { - return Component->GetClass()->IsChildOf() - && HasTag(Component, TagTransform); + return Component->GetClass()->IsChildOf() && HasTag(Component, TagTransform); } static bool StoresTags(const UActorComponent* Component) @@ -98,10 +96,22 @@ struct FSELevelFilter return Tag == TagNoTransform || Tag == TagNoPhysics || Tag == TagNoTags; } - static FORCEINLINE bool StoresTransform(const AActor* Actor) { return Actor->IsRootComponentMovable() && !HasTag(Actor, TagNoTransform); } - static FORCEINLINE bool StoresPhysics(const AActor* Actor) { return !HasTag(Actor, TagNoPhysics); } - static FORCEINLINE bool StoresTags(const AActor* Actor) { return !HasTag(Actor, TagNoTags); } - static FORCEINLINE bool IsProcedural(const AActor* Actor) { return Actor->HasAnyFlags(RF_WasLoaded | RF_LoadCompleted); } + static FORCEINLINE bool StoresTransform(const AActor* Actor) + { + return Actor->IsRootComponentMovable() && !HasTag(Actor, TagNoTransform); + } + static FORCEINLINE bool StoresPhysics(const AActor* Actor) + { + return !HasTag(Actor, TagNoPhysics); + } + static FORCEINLINE bool StoresTags(const AActor* Actor) + { + return !HasTag(Actor, TagNoTags); + } + static FORCEINLINE bool IsProcedural(const AActor* Actor) + { + return Actor->HasAnyFlags(RF_WasLoaded | RF_LoadCompleted); + } static FORCEINLINE bool HasTag(const AActor* Actor, const FName Tag) { @@ -109,7 +119,7 @@ struct FSELevelFilter return Actor->ActorHasTag(Tag); } - static FORCEINLINE bool HasTag(const UActorComponent * Component, const FName Tag) + static FORCEINLINE bool HasTag(const UActorComponent* Component, const FName Tag) { check(Component); return Component->ComponentHasTag(Tag); diff --git a/Source/SaveExtension/Public/LevelStreamingNotifier.h b/Source/SaveExtension/Public/LevelStreamingNotifier.h index f7ffcf5..aee208c 100644 --- a/Source/SaveExtension/Public/LevelStreamingNotifier.h +++ b/Source/SaveExtension/Public/LevelStreamingNotifier.h @@ -1,16 +1,18 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include #include + #include "LevelStreamingNotifier.generated.h" -DECLARE_DELEGATE_OneParam(FLevelNotifierLoaded, ULevelStreaming* ); -DECLARE_DELEGATE_OneParam(FLevelNotifierUnloaded, ULevelStreaming* ); -DECLARE_DELEGATE_OneParam(FLevelNotifierShown, ULevelStreaming* ); -DECLARE_DELEGATE_OneParam(FLevelNotifierHidden, ULevelStreaming* ); + +DECLARE_DELEGATE_OneParam(FLevelNotifierLoaded, ULevelStreaming*); +DECLARE_DELEGATE_OneParam(FLevelNotifierUnloaded, ULevelStreaming*); +DECLARE_DELEGATE_OneParam(FLevelNotifierShown, ULevelStreaming*); +DECLARE_DELEGATE_OneParam(FLevelNotifierHidden, ULevelStreaming*); /** ULevelStreamingNotifier is an adapter that expands UE4's native @@ -22,7 +24,6 @@ class SAVEEXTENSION_API ULevelStreamingNotifier : public UObject GENERATED_BODY() public: - void SetLevelStreaming(ULevelStreaming* InLevelStreaming) { UnBind(); @@ -38,13 +39,24 @@ class SAVEEXTENSION_API ULevelStreamingNotifier : public UObject } } - FLevelNotifierLoaded& OnLevelLoaded() { return LoadedDelegate; } - FLevelNotifierUnloaded& OnLevelUnloaded() { return UnloadedDelegate; } - FLevelNotifierShown& OnLevelShown() { return ShownDelegate; } - FLevelNotifierHidden& OnLevelHidden() { return HiddenDelegate; } + FLevelNotifierLoaded& OnLevelLoaded() + { + return LoadedDelegate; + } + FLevelNotifierUnloaded& OnLevelUnloaded() + { + return UnloadedDelegate; + } + FLevelNotifierShown& OnLevelShown() + { + return ShownDelegate; + } + FLevelNotifierHidden& OnLevelHidden() + { + return HiddenDelegate; + } private: - void UnBind() { if (LevelStreaming.IsValid()) @@ -68,29 +80,33 @@ class SAVEEXTENSION_API ULevelStreamingNotifier : public UObject // Associated Level Streaming TWeakObjectPtr LevelStreaming; - FLevelNotifierLoaded LoadedDelegate; + FLevelNotifierLoaded LoadedDelegate; FLevelNotifierUnloaded UnloadedDelegate; - FLevelNotifierShown ShownDelegate; - FLevelNotifierHidden HiddenDelegate; + FLevelNotifierShown ShownDelegate; + FLevelNotifierHidden HiddenDelegate; UFUNCTION() - void OnLoaded() { + void OnLoaded() + { LoadedDelegate.ExecuteIfBound(LevelStreaming.Get()); } UFUNCTION() - void OnUnloaded() { + void OnUnloaded() + { UnloadedDelegate.ExecuteIfBound(LevelStreaming.Get()); } UFUNCTION() - void OnShown() { + void OnShown() + { ShownDelegate.ExecuteIfBound(LevelStreaming.Get()); } UFUNCTION() - void OnHidden() { + void OnHidden() + { HiddenDelegate.ExecuteIfBound(LevelStreaming.Get()); } }; diff --git a/Source/SaveExtension/Public/LifetimeComponent.h b/Source/SaveExtension/Public/LifetimeComponent.h index 7fe7a00..0475315 100644 --- a/Source/SaveExtension/Public/LifetimeComponent.h +++ b/Source/SaveExtension/Public/LifetimeComponent.h @@ -1,16 +1,17 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include -#include - #include "SaveExtensionInterface.h" #include "SaveManager.h" +#include +#include + #include "LifetimeComponent.generated.h" + DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLifetimeStartSignature); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLifetimeSavedSignature); @@ -20,7 +21,6 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLifetimeResumeSignature); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLifetimeFinishSignature); - /** * Controls the complete saving and loading process */ @@ -33,7 +33,6 @@ class SAVEEXTENSION_API ULifetimeComponent : public UActorComponent, public ISav /* METHODS */ /************************************************************************/ public: - ULifetimeComponent(); virtual void BeginPlay() override; @@ -57,7 +56,6 @@ class SAVEEXTENSION_API ULifetimeComponent : public UActorComponent, public ISav /* EVENTS */ /***********************************************************************/ protected: - // Called once when this actor gets created for the first time. Similar to BeginPlay UPROPERTY(BlueprintAssignable, Category = SaveExtension) FLifetimeStartSignature Start; @@ -76,9 +74,20 @@ class SAVEEXTENSION_API ULifetimeComponent : public UActorComponent, public ISav public: - - FLifetimeStartSignature& OnStart() { return Start; } - FLifetimeSavedSignature& OnSaved() { return Saved; } - FLifetimeResumeSignature& OnResume() { return Resume; } - FLifetimeFinishSignature& OnFinish() { return Finish; } + FLifetimeStartSignature& OnStart() + { + return Start; + } + FLifetimeSavedSignature& OnSaved() + { + return Saved; + } + FLifetimeResumeSignature& OnResume() + { + return Resume; + } + FLifetimeFinishSignature& OnFinish() + { + return Finish; + } }; diff --git a/Source/SaveExtension/Public/Misc/ClassFilter.h b/Source/SaveExtension/Public/Misc/ClassFilter.h index b31896b..06e1b16 100644 --- a/Source/SaveExtension/Public/Misc/ClassFilter.h +++ b/Source/SaveExtension/Public/Misc/ClassFilter.h @@ -1,26 +1,27 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "CoreMinimal.h" -#include + #include +#include + #include "ClassFilter.generated.h" + USTRUCT(BlueprintType) struct SAVEEXTENSION_API FSEClassFilter { GENERATED_BODY() private: - // Used from editor side to limit displayed classes UPROPERTY() UClass* BaseClass; public: - /** This classes are allowed (and their children) */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization") TSet> AllowedClasses; @@ -30,13 +31,11 @@ struct SAVEEXTENSION_API FSEClassFilter TSet> IgnoredClasses; protected: - UPROPERTY(Transient) mutable TSet BakedAllowedClasses; public: - FSEClassFilter() : FSEClassFilter(UObject::StaticClass()) {} FSEClassFilter(UClass* const BaseClass); @@ -52,7 +51,10 @@ struct SAVEEXTENSION_API FSEClassFilter return BakedAllowedClasses.Contains(Class); } - FORCEINLINE UClass* GetBaseClass() const { return BaseClass; } + FORCEINLINE UClass* GetBaseClass() const + { + return BaseClass; + } FString ToString(); void FromString(FString String); @@ -70,13 +72,14 @@ struct FSEActorClassFilter FSEClassFilter ClassFilter; - FSEActorClassFilter() - : ClassFilter(AActor::StaticClass()) - {} + FSEActorClassFilter() : ClassFilter(AActor::StaticClass()) {} FSEActorClassFilter(TSubclassOf actorClass) : ClassFilter(actorClass) {} /** Bakes a set of allowed classes based on the current settings */ - void BakeAllowedClasses() const { ClassFilter.BakeAllowedClasses(); } + void BakeAllowedClasses() const + { + ClassFilter.BakeAllowedClasses(); + } FORCEINLINE bool IsClassAllowed(UClass* const Class) const { @@ -94,13 +97,14 @@ struct FSEComponentClassFilter FSEClassFilter ClassFilter; - FSEComponentClassFilter() - : ClassFilter(UActorComponent::StaticClass()) - {} + FSEComponentClassFilter() : ClassFilter(UActorComponent::StaticClass()) {} FSEComponentClassFilter(TSubclassOf compClass) : ClassFilter(compClass) {} /** Bakes a set of allowed classes based on the current settings */ - void BakeAllowedClasses() const { ClassFilter.BakeAllowedClasses(); } + void BakeAllowedClasses() const + { + ClassFilter.BakeAllowedClasses(); + } FORCEINLINE bool IsClassAllowed(UClass* const Class) const { diff --git a/Source/SaveExtension/Public/Misc/SlotHelpers.h b/Source/SaveExtension/Public/Misc/SlotHelpers.h index 31440d8..ae660c1 100644 --- a/Source/SaveExtension/Public/Misc/SlotHelpers.h +++ b/Source/SaveExtension/Public/Misc/SlotHelpers.h @@ -1,11 +1,12 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "FileAdapter.h" + #include #include -#include "FileAdapter.h" struct FSlotHelpers @@ -18,9 +19,7 @@ struct FSlotHelpers public: TArray& FoundSlots; - FFindSlotVisitor(TArray& FoundSlots) - : FoundSlots(FoundSlots) - {} + FFindSlotVisitor(TArray& FoundSlots) : FoundSlots(FoundSlots) {} virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override; }; diff --git a/Source/SaveExtension/Public/Misc/TypeTraits.h b/Source/SaveExtension/Public/Misc/TypeTraits.h index e6dc8f1..e466846 100644 --- a/Source/SaveExtension/Public/Misc/TypeTraits.h +++ b/Source/SaveExtension/Public/Misc/TypeTraits.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -6,23 +6,27 @@ template -constexpr bool VariadicContainsType() { +constexpr bool VariadicContainsType() +{ return false; }; template -constexpr bool VariadicContainsType() { +constexpr bool VariadicContainsType() +{ return TIsSame::Value || VariadicContainsType(); }; template -constexpr uint32 GetVariadicTypeIndex() { - return Index+1; +constexpr uint32 GetVariadicTypeIndex() +{ + return Index + 1; }; template -constexpr uint32 GetVariadicTypeIndex() { +constexpr uint32 GetVariadicTypeIndex() +{ if (TIsSame::Value) return Index; else diff --git a/Source/SaveExtension/Public/Multithreading/Delegates.h b/Source/SaveExtension/Public/Multithreading/Delegates.h index 97576dd..c373048 100644 --- a/Source/SaveExtension/Public/Multithreading/Delegates.h +++ b/Source/SaveExtension/Public/Multithreading/Delegates.h @@ -1,11 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include -DECLARE_DELEGATE_OneParam(FOnSlotInfosLoaded, const TArray&); +DECLARE_DELEGATE_OneParam(FOnSlotInfosLoaded, const TArray&); // @param Amount of slots removed DECLARE_DELEGATE(FOnSlotsDeleted); diff --git a/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h b/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h index 10e20bd..b36b548 100644 --- a/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h +++ b/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h @@ -1,13 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "FileAdapter.h" #include "Multithreading/Delegates.h" -#include "SlotInfo.h" +#include "SaveSlot.h" -#include #include +#include class USaveManager; @@ -16,14 +16,13 @@ class USaveManager; * FDeleteSlotsTask * Async task to remove an specific or all slots */ -class FDeleteSlotsTask : public FNonAbandonableTask { +class FDeleteSlotsTask : public FNonAbandonableTask +{ protected: - const USaveManager* const Manager = nullptr; FString SpecificSlotName; public: - bool bSuccess = false; /** All infos Constructor */ @@ -37,6 +36,5 @@ class FDeleteSlotsTask : public FNonAbandonableTask { } private: - - USlotInfo* LoadInfoFromFile(const FString Name) const; + USaveSlot* LoadInfoFromFile(const FString Name) const; }; diff --git a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h index f2f5ef6..d8e850c 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h @@ -1,10 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include #include "FileAdapter.h" +#include + ///////////////////////////////////////////////////// // FLoadFileTask @@ -12,27 +13,23 @@ class FLoadFileTask : public FNonAbandonableTask { protected: - TWeakObjectPtr Manager; const FString SlotName; - TWeakObjectPtr SlotInfo; - TWeakObjectPtr SlotData; + TWeakObjectPtr SlotInfo; + TWeakObjectPtr SlotData; public: - - explicit FLoadFileTask(USaveManager* Manager, FStringView SlotName) - : Manager(Manager) - , SlotName(SlotName) + explicit FLoadFileTask(USaveManager* Manager, FStringView SlotName) : Manager(Manager), SlotName(SlotName) {} ~FLoadFileTask() { - if(SlotInfo.IsValid()) + if (SlotInfo.IsValid()) { SlotInfo->ClearInternalFlags(EInternalObjectFlags::Async); } - if(SlotData.IsValid()) + if (SlotData.IsValid()) { SlotData->ClearInternalFlags(EInternalObjectFlags::Async); } @@ -41,7 +38,7 @@ class FLoadFileTask : public FNonAbandonableTask void DoWork() { FScopedFileReader FileReader(FFileAdapter::GetSlotPath(SlotName)); - if(FileReader.IsValid()) + if (FileReader.IsValid()) { FSaveFile File; File.Read(FileReader, false); @@ -50,12 +47,12 @@ class FLoadFileTask : public FNonAbandonableTask } } - USlotInfo* GetInfo() + USaveSlot* GetInfo() { return SlotInfo.Get(); } - USlotData* GetData() + USaveSlotData* GetData() { return SlotData.Get(); } diff --git a/Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h b/Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h index 9082a13..773ca9a 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h @@ -1,12 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include #include "FileAdapter.h" #include "Multithreading/Delegates.h" +#include "SaveSlot.h" + +#include -#include "SlotInfo.h" class USaveManager; @@ -18,31 +19,28 @@ class USaveManager; class FLoadSlotInfosTask : public FNonAbandonableTask { protected: - const USaveManager* Manager; const bool bSortByRecent = false; // If not empty, only this specific slot will be loaded const FName SlotName; - TArray LoadedSlots; + TArray LoadedSlots; FOnSlotInfosLoaded Delegate; public: - /** All infos Constructor */ - explicit FLoadSlotInfosTask(const USaveManager* Manager, bool bInSortByRecent, const FOnSlotInfosLoaded& Delegate) + explicit FLoadSlotInfosTask( + const USaveManager* Manager, bool bInSortByRecent, const FOnSlotInfosLoaded& Delegate) : Manager(Manager) , bSortByRecent(bInSortByRecent) , Delegate(Delegate) {} /** One info Constructor */ - explicit FLoadSlotInfosTask(USaveManager* Manager, FName SlotName) - : Manager(Manager) - , SlotName(SlotName) + explicit FLoadSlotInfosTask(USaveManager* Manager, FName SlotName) : Manager(Manager), SlotName(SlotName) {} void DoWork(); @@ -50,7 +48,7 @@ class FLoadSlotInfosTask : public FNonAbandonableTask /** Called after the task has finished */ void AfterFinish(); - const TArray& GetLoadedSlots() const + const TArray& GetLoadedSlots() const { return LoadedSlots; } diff --git a/Source/SaveExtension/Public/Multithreading/SaveFileTask.h b/Source/SaveExtension/Public/Multithreading/SaveFileTask.h index 2162649..a3be359 100644 --- a/Source/SaveExtension/Public/Multithreading/SaveFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/SaveFileTask.h @@ -1,34 +1,32 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include #include "FileAdapter.h" +#include + ///////////////////////////////////////////////////// // FSaveFileTask // Async task to save a File -class FSaveFileTask : public FNonAbandonableTask { +class FSaveFileTask : public FNonAbandonableTask +{ protected: - - USlotInfo* Info; - USlotData* Data; + USaveSlot* Info; const FString SlotName; const bool bUseCompression; public: - - FSaveFileTask(USlotInfo* Info, USlotData* Data, const FString& InSlotName, const bool bInUseCompression) : - Info(Info), - Data(Data), - SlotName(InSlotName), - bUseCompression(bInUseCompression) + FSaveFileTask(USaveSlot* Info, const FString& InSlotName, const bool bInUseCompression) + : Info(Info) + , SlotName(InSlotName) + , bUseCompression(bInUseCompression) {} void DoWork() { - FFileAdapter::SaveFile(SlotName, Info, Data, bUseCompression); + FFileAdapter::SaveFile(SlotName, Info, bUseCompression); } FORCEINLINE TStatId GetStatId() const diff --git a/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h b/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h index 0c11117..6a47800 100644 --- a/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h +++ b/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -11,9 +11,7 @@ class ITaskHolder public: virtual bool Tick() = 0; virtual void Cancel(bool bFinishSynchronously) = 0; - virtual ~ITaskHolder() - { - } + virtual ~ITaskHolder() {} }; template @@ -27,17 +25,13 @@ class FTaskHolder : public FAsyncTask, public ITaskHolder bool bNotified = false; FFinishedEvent _OnFinished; - FTaskHolder() : ITaskHolder(), Super() - { - } - virtual ~FTaskHolder() - { - } + FTaskHolder() : ITaskHolder(), Super() {} + virtual ~FTaskHolder() {} template - FTaskHolder(ArgTypes&&... CtrArgs) : Super(Forward(CtrArgs)...), ITaskHolder() - { - } + FTaskHolder(ArgTypes&&... CtrArgs) : Super(Forward(CtrArgs)...) + , ITaskHolder() + {} auto& OnFinished(TFunction&)> Delegate) { @@ -74,7 +68,6 @@ class FTaskHolder : public FAsyncTask, public ITaskHolder } private: - void TryNotifyFinish() { if (!bNotified) @@ -91,9 +84,7 @@ class FScopedTaskList TArray> Tasks; public: - FScopedTaskList() - { - } + FScopedTaskList() {} template inline FTaskHolder& CreateTask(ArgTypes&&... CtrArgs) @@ -107,7 +98,9 @@ class FScopedTaskList void Tick() { // Tick all running tasks and remove the ones that finished - Tasks.RemoveAllSwap([](auto& Task) { return Task->Tick(); }); + Tasks.RemoveAllSwap([](auto& Task) { + return Task->Tick(); + }); } void CancelAll() diff --git a/Source/SaveExtension/Public/SaveExtensionInterface.h b/Source/SaveExtension/Public/SaveExtensionInterface.h index 32cec10..2554d8f 100644 --- a/Source/SaveExtension/Public/SaveExtensionInterface.h +++ b/Source/SaveExtension/Public/SaveExtensionInterface.h @@ -1,13 +1,15 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "LevelFilter.h" + #include -#include "LevelFilter.h" #include "SaveExtensionInterface.generated.h" + UINTERFACE(Category = SaveExtension, BlueprintType) class SAVEEXTENSION_API USaveExtensionInterface : public UInterface { @@ -16,11 +18,9 @@ class SAVEEXTENSION_API USaveExtensionInterface : public UInterface class SAVEEXTENSION_API ISaveExtensionInterface { - GENERATED_BODY() public: - /** BP API **/ // Event called when Save process starts diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 5af97e8..4d1c1c2 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -7,13 +7,13 @@ #include "LatentActions/LoadGameAction.h" #include "LatentActions/SaveGameAction.h" #include "LevelStreamingNotifier.h" -#include "Multithreading/ScopedTaskManager.h" #include "Multithreading/Delegates.h" +#include "Multithreading/ScopedTaskManager.h" #include "SaveExtensionInterface.h" #include "SavePreset.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" #include "Serialization/SlotDataTask.h" -#include "SlotData.h" -#include "SlotInfo.h" #include #include @@ -26,8 +26,8 @@ #include "SaveManager.generated.h" -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USlotInfo*, SlotInfo); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USlotInfo*, SlotInfo); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, SlotInfo); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, SlotInfo); struct FLatentActionInfo; @@ -56,30 +56,24 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi { GENERATED_BODY() - friend USlotDataTask; + friend USaveSlotDataTask; /************************************************************************/ /* PROPERTIES */ /************************************************************************/ public: - // Loaded from settings. Can be changed at runtime - UPROPERTY(Transient, BlueprintReadWrite, Category=SaveManager) + UPROPERTY(Transient, BlueprintReadWrite, Category = SaveManager) bool bTickWithGameWorld = false; private: UPROPERTY(Transient) USavePreset* ActivePreset; - /** Currently loaded SaveInfo. SaveInfo stores basic information about a saved game. Played time, levels, - * progress, etc. */ - UPROPERTY() - USlotInfo* CurrentInfo; - - /** Currently loaded SaveData. SaveData stores all serialized info about the world. */ + // Active SaveSlot used for current saves (load on start, periodic save, etc) UPROPERTY() - USlotData* CurrentData; + TObjectPtr ActiveSlot; /** The game instance to which this save manager is owned. */ TWeakObjectPtr OwningGameInstance; @@ -93,7 +87,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi TArray> SubscribedInterfaces; UPROPERTY(Transient) - TArray Tasks; + TArray Tasks; /************************************************************************/ @@ -121,7 +115,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Save the Game info an SlotInfo */ - bool SaveSlot(const USlotInfo* SlotInfo, bool bOverrideIfNeeded = true, bool bScreenshot = false, + bool SaveSlot(const USaveSlot* SlotInfo, bool bOverrideIfNeeded = true, bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Save the Game into an specified slot id */ @@ -129,7 +123,8 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Save the currently loaded Slot */ - bool SaveCurrentSlot(bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); + bool SaveCurrentSlot( + bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Load game from a file name */ @@ -139,12 +134,12 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi bool LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded = {}); /** Load game from a SlotInfo */ - bool LoadSlot(const USlotInfo* SlotInfo, FOnGameLoaded OnLoaded = {}); + bool LoadSlot(const USaveSlot* SlotInfo, FOnGameLoaded OnLoaded = {}); /** Reload the currently loaded slot if any */ bool ReloadCurrentSlot(FOnGameLoaded OnLoaded = {}) { - return LoadSlot(CurrentInfo, MoveTemp(OnLoaded)); + return LoadSlot(ActiveSlot, MoveTemp(OnLoaded)); } /** @@ -152,8 +147,8 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi * @param bSortByRecent Should slots be ordered by save date? * @param SaveInfos All saved games found on disk */ - void LoadAllSlotInfos(bool bSortByRecent, FOnSlotInfosLoaded Delegate); - void LoadAllSlotInfosSync(bool bSortByRecent, FOnSlotInfosLoaded Delegate); + void FindAllSlots(bool bSortByRecent, FOnSlotInfosLoaded Delegate); + void FindAllSlotsSync(bool bSortByRecent, TArray& Slots); /** Delete a saved game on an specified slot name * Performance: Interacts with disk, can be slow @@ -170,33 +165,40 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi // with the normal C++ API /** Save the Game into an specified Slot */ - UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (AdvancedDisplay = "bScreenshot, Size", - DisplayName = "Save Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlot(FName SlotName, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); + UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, + meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot", Latent, + LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + void BPSaveSlot(FName SlotName, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, + FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game into an specified Slot */ - UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (AdvancedDisplay = "bScreenshot, Size", - DisplayName = "Save Slot by Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); + UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, + meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Id", Latent, + LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + void BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, + FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game to a Slot */ - UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (AdvancedDisplay = "bScreenshot, Size", - DisplayName = "Save Slot by Info", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotByInfo(const USlotInfo* SlotInfo, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); + UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, + meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Info", Latent, + LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + void BPSaveSlotByInfo(const USaveSlot* SlotInfo, bool bScreenshot, const FScreenshotSize Size, + ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the currently loaded Slot */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Saving", meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Current Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveCurrentSlot(bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo) + void BPSaveCurrentSlot( + bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo) { - BPSaveSlotByInfo(CurrentInfo, bScreenshot, Size, Result, MoveTemp(LatentInfo), true); + BPSaveSlotByInfo(ActiveSlot, bScreenshot, Size, Result, MoveTemp(LatentInfo), true); } /** Load game from a slot name */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Load Slot", Latent, LatentInfo = "LatentInfo", - ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + meta = (DisplayName = "Load Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", + UnsafeDuringActorConstruction)) void BPLoadSlot(FName SlotName, ELoadGameResult& Result, FLatentActionInfo LatentInfo); /** Load game from a slot Id */ @@ -207,9 +209,9 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Load game from a SlotInfo */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Load Slot by Info", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", - UnsafeDuringActorConstruction)) - void BPLoadSlotByInfo(const USlotInfo* SlotInfo, ELoadGameResult& Result, FLatentActionInfo LatentInfo); + meta = (DisplayName = "Load Slot by Info", Latent, LatentInfo = "LatentInfo", + ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + void BPLoadSlotByInfo(const USaveSlot* SlotInfo, ELoadGameResult& Result, FLatentActionInfo LatentInfo); /** Reload the currently loaded slot if any */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", @@ -217,7 +219,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPReloadCurrentSlot(ELoadGameResult& Result, FLatentActionInfo LatentInfo) { - BPLoadSlotByInfo(CurrentInfo, Result, MoveTemp(LatentInfo)); + BPLoadSlotByInfo(ActiveSlot, Result, MoveTemp(LatentInfo)); } /** @@ -227,8 +229,8 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi */ UFUNCTION(BlueprintCallable, Category = "SaveExtension", meta = (Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", - DisplayName = "Load All Slot Infos")) - void BPLoadAllSlotInfos(const bool bSortByRecent, TArray& SaveInfos, ELoadInfoResult& Result, + DisplayName = "Find All Slots")) + void BPFindAllSlots(const bool bSortByRecent, TArray& Slots, ELoadInfoResult& Result, struct FLatentActionInfo LatentInfo); /** Delete a saved game on an specified slot Id @@ -241,7 +243,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi { return false; } - return DeleteSlot(GetSlotNameFromId(SlotId)); + return DeleteSlot(GetFileNameFromId(SlotId)); } /** Delete all saved slots from disk, loaded or not */ @@ -250,39 +252,24 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi DisplayName = "Delete All Slots")) void BPDeleteAllSlots(EDeleteSlotsResult& Result, FLatentActionInfo LatentInfo); - UFUNCTION(BlueprintPure, Category = "SaveExtension") - USavePreset* BPGetPreset() const - { - return ActivePreset; - } - /** BLUEPRINTS & C++ API */ public: - /** Delete a saved game on an specified slot * Performance: Interacts with disk, can be slow */ UFUNCTION(BlueprintCallable, Category = "SaveExtension") - bool DeleteSlot(USlotInfo* Slot) + bool DeleteSlot(USaveSlot* Slot) { - return Slot? DeleteSlot(Slot->FileName) : false; + return Slot ? DeleteSlot(Slot->FileName) : false; } /** Get the currently loaded SlotInfo. If game was never loaded returns a new SlotInfo */ UFUNCTION(BlueprintPure, Category = "SaveExtension") - FORCEINLINE USlotInfo* GetCurrentInfo() - { - TryInstantiateInfo(); - return CurrentInfo; - } - - /** Get the currently loaded SlotData. If game was never loaded returns an empty SlotData */ - UFUNCTION(BlueprintPure, Category = "SaveExtension") - FORCEINLINE USlotData* GetCurrentData() + FORCEINLINE USaveSlot* GetActiveSlot() { - TryInstantiateInfo(); - return CurrentData; + AssureActiveSlot(); + return ActiveSlot; } /** @@ -292,13 +279,13 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi * @return the SlotInfo associated with an Id */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") - FORCEINLINE USlotInfo* GetSlotInfoById(int32 SlotId) + FORCEINLINE USaveSlot* GetSlotInfoById(int32 SlotId) { return LoadInfo(SlotId); } UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") - FORCEINLINE USlotInfo* GetSlotInfo(FName SlotName) + FORCEINLINE USaveSlot* GetSlotInfo(FName SlotName) { return LoadInfo(SlotName); } @@ -315,7 +302,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintPure, Category = "SaveExtension|Slots") bool IsSlotSavedById(int32 SlotId) const { - return IsValidSlot(SlotId)? IsSlotSaved(GetSlotNameFromId(SlotId)) : false; + return IsValidSlot(SlotId) ? IsSlotSaved(GetFileNameFromId(SlotId)) : false; } /** Check if currently playing in a saved slot @@ -324,39 +311,26 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintPure, Category = "SaveExtension|Slots") FORCEINLINE bool IsInSlot() const { - return CurrentInfo && CurrentData; + return ActiveSlot != nullptr; } - /** - * Set the preset to be used for saving and loading - * @return true if the preset was set successfully - */ - UFUNCTION(BlueprintCallable, Category = "SaveExtension") - USavePreset* SetActivePreset(TSubclassOf PresetClass); - - const USavePreset* GetPreset() const; - - void TryInstantiateInfo(bool bForced = false); + void AssureActiveSlot(bool bForced = false); UFUNCTION(BlueprintPure, Category = "SaveExtension") - FName GetSlotNameFromId(const int32 SlotId) const; + FName GetFileNameFromId(const int32 SlotId) const; bool IsValidSlot(const int32 Slot) const; - void __SetCurrentInfo(USlotInfo* NewInfo) - { - CurrentInfo = NewInfo; - } - - void __SetCurrentData(USlotData* NewData) + void __SetActiveSlot(USaveSlot* NewInfo) { - CurrentData = NewData; + ActiveSlot = NewInfo; } - USlotInfo* LoadInfo(FName SlotName); - USlotInfo* LoadInfo(uint32 SlotId) + USaveSlot* CreateInfo(); + USaveSlot* LoadInfo(FName FileName); + USaveSlot* LoadInfo(uint32 SlotId) { - return IsValidSlot(SlotId)? LoadInfo(GetSlotNameFromId(SlotId)) : nullptr; + return IsValidSlot(SlotId) ? LoadInfo(GetFileNameFromId(SlotId)) : nullptr; } protected: @@ -374,7 +348,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi void OnLevelLoaded(ULevelStreaming* StreamingLevel) {} - USlotDataTask* CreateTask(TSubclassOf TaskType); + USaveSlotDataTask* CreateTask(TSubclassOf TaskType); template TaskType* CreateTask() @@ -382,7 +356,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi return Cast(CreateTask(TaskType::StaticClass())); } - void FinishTask(USlotDataTask* Task); + void FinishTask(USaveSlotDataTask* Task); public: bool HasTasks() const @@ -455,15 +429,20 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /* DEPRECATED */ /***********************************************************************/ - UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (DeprecatedFunction, DeprecationMessage="Use 'Save Slot by Id' instead.", - AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot to Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotToId(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true) + UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, + meta = (DeprecatedFunction, DeprecationMessage = "Use 'Save Slot by Id' instead.", + AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot to Id", Latent, + LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + void BPSaveSlotToId(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, + FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true) { BPSaveSlotById(SlotId, bScreenshot, Size, Result, LatentInfo, bOverrideIfNeeded); } - UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", meta = (DeprecatedFunction, DeprecationMessage="Use 'Load Slot by Id' instead.", - DisplayName = "Load Slot from Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) + UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", + meta = (DeprecatedFunction, DeprecationMessage = "Use 'Load Slot by Id' instead.", + DisplayName = "Load Slot from Id", Latent, LatentInfo = "LatentInfo", + ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPLoadSlotFromId(int32 SlotId, ELoadGameResult& Result, FLatentActionInfo LatentInfo) { BPLoadSlotById(SlotId, Result, LatentInfo); @@ -471,18 +450,18 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi }; -inline bool USaveManager::SaveSlot(int32 SlotId, bool bOverrideIfNeeded, bool bScreenshot, - const FScreenshotSize Size, FOnGameSaved OnSaved) +inline bool USaveManager::SaveSlot( + int32 SlotId, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { if (!IsValidSlot(SlotId)) { - SELog(GetPreset(), "Invalid Slot. Cant go under 0 or exceed MaxSlots.", true); + UE_LOG(LogSaveExtension, Error, TEXT("Can't save to slot id under 0 or exceeding MaxSlots.")); return false; } - return SaveSlot(GetSlotNameFromId(SlotId), bOverrideIfNeeded, bScreenshot, Size, OnSaved); + return SaveSlot(GetFileNameFromId(SlotId), bOverrideIfNeeded, bScreenshot, Size, OnSaved); } -inline bool USaveManager::SaveSlot(const USlotInfo* SlotInfo, bool bOverrideIfNeeded, bool bScreenshot, +inline bool USaveManager::SaveSlot(const USaveSlot* SlotInfo, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { if (!SlotInfo) @@ -492,19 +471,21 @@ inline bool USaveManager::SaveSlot(const USlotInfo* SlotInfo, bool bOverrideIfNe return SaveSlot(SlotInfo->FileName, bOverrideIfNeeded, bScreenshot, Size, OnSaved); } -inline void USaveManager::BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) +inline void USaveManager::BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, + ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { if (!IsValidSlot(SlotId)) { - SELog(GetPreset(), "Invalid Slot. Cant go under 0 or exceed MaxSlots.", true); + UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Cant go under 0 or exceed MaxSlots.")); Result = ESaveGameResult::Failed; return; } - BPSaveSlot(GetSlotNameFromId(SlotId), bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); + BPSaveSlot(GetFileNameFromId(SlotId), bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } -inline void USaveManager::BPSaveSlotByInfo(const USlotInfo* SlotInfo, bool bScreenshot, const FScreenshotSize Size, - ESaveGameResult& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) +inline void USaveManager::BPSaveSlotByInfo(const USaveSlot* SlotInfo, bool bScreenshot, + const FScreenshotSize Size, ESaveGameResult& Result, struct FLatentActionInfo LatentInfo, + bool bOverrideIfNeeded) { if (!SlotInfo) { @@ -517,20 +498,20 @@ inline void USaveManager::BPSaveSlotByInfo(const USlotInfo* SlotInfo, bool bScre /** Save the currently loaded Slot */ inline bool USaveManager::SaveCurrentSlot(bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { - return SaveSlot(CurrentInfo, true, bScreenshot, Size, OnSaved); + return SaveSlot(ActiveSlot, true, bScreenshot, Size, OnSaved); } inline bool USaveManager::LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded) { if (!IsValidSlot(SlotId)) { - SELog(GetPreset(), "Invalid Slot. Can't go under 0 or exceed MaxSlots.", true); + UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Can't go under 0 or exceed MaxSlots.")); return false; } - return LoadSlot(GetSlotNameFromId(SlotId), OnLoaded); + return LoadSlot(GetFileNameFromId(SlotId), OnLoaded); } -inline bool USaveManager::LoadSlot(const USlotInfo* SlotInfo, FOnGameLoaded OnLoaded) +inline bool USaveManager::LoadSlot(const USaveSlot* SlotInfo, FOnGameLoaded OnLoaded) { if (!SlotInfo) { @@ -542,10 +523,11 @@ inline bool USaveManager::LoadSlot(const USlotInfo* SlotInfo, FOnGameLoaded OnLo inline void USaveManager::BPLoadSlotById( int32 SlotId, ELoadGameResult& Result, struct FLatentActionInfo LatentInfo) { - BPLoadSlot(GetSlotNameFromId(SlotId), Result, MoveTemp(LatentInfo)); + BPLoadSlot(GetFileNameFromId(SlotId), Result, MoveTemp(LatentInfo)); } -inline void USaveManager::BPLoadSlotByInfo(const USlotInfo* SlotInfo, ELoadGameResult& Result, FLatentActionInfo LatentInfo) +inline void USaveManager::BPLoadSlotByInfo( + const USaveSlot* SlotInfo, ELoadGameResult& Result, FLatentActionInfo LatentInfo) { if (!SlotInfo) { @@ -555,11 +537,6 @@ inline void USaveManager::BPLoadSlotByInfo(const USlotInfo* SlotInfo, ELoadGameR BPLoadSlot(SlotInfo->FileName, Result, MoveTemp(LatentInfo)); } -inline bool USaveManager::IsValidSlot(const int32 Slot) const -{ - return GetPreset()->IsValidId(Slot); -} - inline void USaveManager::IterateSubscribedInterfaces(TFunction&& Callback) { for (const TScriptInterface& Interface : SubscribedInterfaces) @@ -588,7 +565,7 @@ inline bool USaveManager::IsTickable() const inline UWorld* USaveManager::GetTickableGameObjectWorld() const { - return bTickWithGameWorld? GetWorld() : nullptr; + return bTickWithGameWorld ? GetWorld() : nullptr; } inline TStatId USaveManager::GetStatId() const diff --git a/Source/SaveExtension/Public/SavePreset.h b/Source/SaveExtension/Public/SavePreset.h index 16713bb..da71d88 100644 --- a/Source/SaveExtension/Public/SavePreset.h +++ b/Source/SaveExtension/Public/SavePreset.h @@ -1,32 +1,34 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Engine/DataAsset.h" +#include "Misc/ClassFilter.h" #include "UObject/NoExportTypes.h" -#include "Misc/ClassFilter.h" #include "SavePreset.generated.h" /** -* Specifies the behavior while saving or loading -*/ + * Specifies the behavior while saving or loading + */ UENUM() -enum class ESaveASyncMode : uint8 { +enum class ESaveASyncMode : uint8 +{ OnlySync, LoadAsync, SaveAsync, SaveAndLoadAsync }; -class USlotInfo; -class USlotData; +class USaveSlot; +class USaveSlotData; /** - * What to save, how to save it, when, every x minutes, what info file, what data file, save by level streaming? + * What to save, how to save it, when, every x minutes, what info file, what data file, save by level + * streaming? */ UCLASS(ClassGroup = SaveExtension, Blueprintable) class SAVEEXTENSION_API USavePreset : public UObject @@ -34,39 +36,6 @@ class SAVEEXTENSION_API USavePreset : public UObject GENERATED_BODY() public: - - /** - * Specify the SaveInfo object to be used with this preset - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) - TSubclassOf SlotInfoClass; - - /** - * Specify the SaveData object to be used with this preset - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) - TSubclassOf SlotDataClass; - - /** Maximum amount of saved slots at the same time */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Gameplay, meta = (ClampMin = "0")) - int32 MaxSlots = 0; - - /** If checked, will attempt to Save Game to first Slot found, timed event. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) - bool bAutoSave = true; - - /** Interval in seconds for auto saving */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay, meta = (EditCondition = "bAutoSave", UIMin = "15", UIMax = "3600")) - int32 AutoSaveInterval = 120.f; - - /** If checked, will attempt to Save Game to first Slot found, timed event. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay, meta = (EditCondition = "bAutoSave")) - bool bSaveOnExit = false; - - /** If checked, will attempt to Load Game from last Slot found, when game starts */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay) - bool bAutoLoad = true; - /** * If checked, will print messages to Log, and Viewport if DebugInScreen is enabled. * Ignored in package or Shipping mode. @@ -78,12 +47,14 @@ class SAVEEXTENSION_API USavePreset : public UObject * If checked and Debug is enabled, will print messages to Viewport. * Ignored in package or Shipping mode. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay, AdvancedDisplay, meta = (EditCondition = "bDebug")) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay, AdvancedDisplay, + meta = (EditCondition = "bDebug")) bool bDebugInScreen = true; /** If true save files will be compressed - * Performance: Can add from 10ms to 20ms to loading and saving (estimate) but reduce file sizes making them up to 30x smaller + * Performance: Can add from 10ms to 20ms to loading and saving (estimate) but reduce file sizes making + * them up to 30x smaller */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Serialization) bool bUseCompression = true; @@ -95,11 +66,13 @@ class SAVEEXTENSION_API USavePreset : public UObject UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors") FSEActorClassFilter ActorFilter; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors", meta = (PinHiddenByDefault, InlineEditConditionToggle)) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors", + meta = (PinHiddenByDefault, InlineEditConditionToggle)) bool bUseLoadActorFilter = false; /** If enabled, this filter will be used while loading instead of "ActorFilter" */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors", meta = (EditCondition="bUseLoadActorFilter")) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors", + meta = (EditCondition = "bUseLoadActorFilter")) FSEActorClassFilter LoadActorFilter; /** If true will store ActorComponents depending on the filters */ @@ -109,15 +82,16 @@ class SAVEEXTENSION_API USavePreset : public UObject UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components") FSEComponentClassFilter ComponentFilter; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components", meta = (PinHiddenByDefault, InlineEditConditionToggle)) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components", + meta = (PinHiddenByDefault, InlineEditConditionToggle)) bool bUseLoadComponentFilter = false; /** If enabled, this filter will be used while loading instead of "ComponentFilter" */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components", meta = (EditCondition = "bUseLoadComponentFilter")) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components", + meta = (EditCondition = "bUseLoadComponentFilter")) FSEComponentClassFilter LoadComponentFilter; public: - /** Serialization will be multi-threaded between all available cores. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous") ESaveASyncMode MultithreadedSerialization = ESaveASyncMode::SaveAsync; @@ -132,9 +106,10 @@ class SAVEEXTENSION_API USavePreset : public UObject /** Max milliseconds to use every frame in an asynchronous operation. * If running at 60Fps (16.6ms), loading or saving asynchronously will add MaxFrameMS: * 16.6ms + 5MS = 21.6ms -> 46Fps - * This means gameplay will not be stopped nor have frame drops while saving or loading. Works best for non multi-threaded platforms + * This means gameplay will not be stopped nor have frame drops while saving or loading. Works best for + * non multi-threaded platforms */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous", meta = (UIMin="3", UIMax="10")) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous", meta = (UIMin = "3", UIMax = "10")) float MaxFrameMs = 5.f; /** Files will be loaded or saved on a secondary thread while game continues */ @@ -143,7 +118,6 @@ class SAVEEXTENSION_API USavePreset : public UObject protected: - /** If true, will Save and Load levels when they are shown or hidden. * This includes level streaming and world composition. */ @@ -152,33 +126,30 @@ class SAVEEXTENSION_API USavePreset : public UObject public: - USavePreset(); - UFUNCTION(BlueprintNativeEvent, Category = "Slots", meta = (DisplayName="Get Slot Name from Id")) + UFUNCTION(BlueprintNativeEvent, Category = "Slots", meta = (DisplayName = "Get SlotName from Id")) void BPGetSlotNameFromId(int32 Id, FName& Name) const; -protected: + UFUNCTION(BlueprintNativeEvent, Category = "Slots", meta = (DisplayName = "Select AutoLoad file name")) + FName BPGetAutoLoadFileName() const; +protected: virtual void GetSlotNameFromId(int32 Id, FName& Name) const; + virtual FName GetAutoLoadFileName() const; /** HELPERS */ public: - - int32 GetMaxIds() const; - - bool IsValidId(int32 Id) const; - UFUNCTION(BlueprintPure, Category = SavePreset) FSEActorClassFilter& GetActorFilter(bool bIsLoading) { - return (bIsLoading && bUseLoadActorFilter)? LoadActorFilter : ActorFilter; + return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; } const FSEActorClassFilter& GetActorFilter(bool bIsLoading) const { - return (bIsLoading && bUseLoadActorFilter)? LoadActorFilter : ActorFilter; + return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; } UFUNCTION(BlueprintPure, Category = SavePreset) @@ -194,45 +165,47 @@ class SAVEEXTENSION_API USavePreset : public UObject bool IsMTSerializationLoad() const { - return MultithreadedSerialization == ESaveASyncMode::LoadAsync || MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedSerialization == ESaveASyncMode::LoadAsync || + MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; } bool IsMTSerializationSave() const { - return MultithreadedSerialization == ESaveASyncMode::SaveAsync || MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedSerialization == ESaveASyncMode::SaveAsync || + MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; } - ESaveASyncMode GetFrameSplitSerialization() const { return FrameSplittedSerialization; } - float GetMaxFrameMs() const { return MaxFrameMs; } + ESaveASyncMode GetFrameSplitSerialization() const + { + return FrameSplittedSerialization; + } + float GetMaxFrameMs() const + { + return MaxFrameMs; + } bool IsFrameSplitLoad() const { - return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESaveASyncMode::LoadAsync || FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); + return !IsMTSerializationLoad() && + (FrameSplittedSerialization == ESaveASyncMode::LoadAsync || + FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); } bool IsFrameSplitSave() const { - return !IsMTSerializationSave() && (FrameSplittedSerialization == ESaveASyncMode::SaveAsync || FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); + return !IsMTSerializationSave() && + (FrameSplittedSerialization == ESaveASyncMode::SaveAsync || + FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); } bool IsMTFilesLoad() const { - return MultithreadedFiles == ESaveASyncMode::LoadAsync || MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedFiles == ESaveASyncMode::LoadAsync || + MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; } bool IsMTFilesSave() const { - return MultithreadedFiles == ESaveASyncMode::SaveAsync || MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedFiles == ESaveASyncMode::SaveAsync || + MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; } struct FSELevelFilter ToFilter() const; }; - - -inline int32 USavePreset::GetMaxIds() const -{ - return MaxSlots <= 0 ? 16384 : MaxSlots; -} - -inline bool USavePreset::IsValidId(int32 Id) const -{ - const int32 MaxIds = GetMaxIds(); - return Id >= 0 && (MaxIds <= 0 || Id < MaxIds); -} diff --git a/Source/SaveExtension/Public/SaveSettings.h b/Source/SaveExtension/Public/SaveSettings.h index 33ad07d..214d652 100644 --- a/Source/SaveExtension/Public/SaveSettings.h +++ b/Source/SaveExtension/Public/SaveSettings.h @@ -1,8 +1,8 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include "SavePreset.h" +#include "SaveSlot.h" #include #include @@ -10,32 +10,20 @@ #include "SaveSettings.generated.h" -UCLASS(ClassGroup = SaveExtension, defaultconfig, config = Game, meta=(DisplayName="Save Extension")) +UCLASS(ClassGroup = SaveExtension, defaultconfig, config = Game, meta = (DisplayName = "Save Extension")) class SAVEEXTENSION_API USaveSettings : public UDeveloperSettings { - GENERATED_BODY() - -protected: - - UPROPERTY(EditAnywhere, Category = "Save Extension", Config, meta = (DisplayName = "Preset")) - TSubclassOf Preset; + GENERATED_BODY() public: - - // If true SaveManager will tick with the world. If game is paused, saving process may be interrupted. - UPROPERTY(EditAnywhere, Category = "Save Extension", Config) - bool bTickWithGameWorld = false; - - - USavePreset* CreatePreset(UObject* Outer) const; + // Active Slot classes are initialized as active slots. + // Active saves represent an always valid save game in memory that can be filled periodically or + // saved. + UPROPERTY(EditAnywhere, Category = "Save Extension", Config) + TSubclassOf ActiveSlot; + + // If true SaveManager will tick with the world, otherwise with the engine. If true, the saving process + // may be interrupted if the game is paused. + UPROPERTY(EditAnywhere, Category = "Save Extension", Config) + bool bTickWithGameWorld = false; }; - - -inline USavePreset* USaveSettings::CreatePreset(UObject* Outer) const -{ - if(UClass* PresetClass = Preset.Get()) - { - return NewObject(Outer, PresetClass); - } - return NewObject(Outer); -} diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h new file mode 100644 index 0000000..a82428b --- /dev/null +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -0,0 +1,151 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#pragma once + +#include "SaveSlotData.h" + +#include +#include +#include + +#include "SaveSlot.generated.h" + + +USTRUCT(Blueprintable) +struct FSaveSlotStats +{ + GENERATED_BODY() + + /** Played time since this saved game was started. Not related to slots, slots can change */ + UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + FTimespan PlayedTime = FTimespan::Zero(); + + /** Played time since this saved game was created */ + UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + FTimespan SlotPlayedTime = FTimespan::Zero(); + + /** Last date at which this slot was saved. */ + UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + FDateTime SaveDate = FDateTime::Now(); + + /** Date at which this slot was loaded. */ + UPROPERTY(BlueprintReadOnly, Transient, Category = SlotInfo) + FDateTime LoadDate; +}; + + +/** + * USaveSlot stores information that needs to be accessible WITHOUT loading the game. + * Works like a common SaveGame object + * E.g: Dates, played time, progress, level + */ +UCLASS(ClassGroup = SaveExtension, hideCategories = ("Activation", "Actor Tick", "Actor", "Input", + "Rendering", "Replication", "Socket", "Thumbnail")) +class SAVEEXTENSION_API USaveSlot : public USaveGame +{ + GENERATED_BODY() + +protected: + /** Begin Settings */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SlotInfo|Settings") + TSubclassOf DataClass = USaveSlotData::StaticClass(); + + /** Maximum amount of saved slots that use this class. 0 is infinite (~16000) */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SlotInfo|Settings", meta = (ClampMin = "0")) + int32 MaxSlots = 0; + + /** If checked, will attempt to Save Game to first Slot found, timed event. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings") + bool bPeriodicSave = true; + + /** Interval in seconds for auto saving */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings", + meta = (EditCondition = "bPeriodicSave", UIMin = "15", UIMax = "3600")) + int32 PeriodicSaveInterval = 120.f; + + /** If checked, will attempt to Save Game to current Slot found, timed event. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings") + bool bSaveOnClose = false; + + /** If checked, will attempt to Load Game from last Slot found, when game starts */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings") + bool bLoadOnStart = true; + /** End Settings */ + + +public: + /** Slot where this SaveInfo and its saveData are saved */ + UPROPERTY(BlueprintReadWrite, Category = SlotInfo) + FName FileName = TEXT("Default"); + + UPROPERTY(BlueprintReadWrite, Category = SlotInfo) + FText DisplayName; + + /** Root Level where this Slot was saved */ + UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + FName Map; + + UPROPERTY(BlueprintReadWrite, Category = SlotInfo) + FSaveSlotStats Stats; + +protected: + UPROPERTY() + FString ThumbnailPath; + + /** Thumbnail gets cached here the first time it is requested */ + UPROPERTY(Transient) + TObjectPtr CachedThumbnail; + + UPROPERTY(BlueprintReadOnly, Transient, Category = SlotInfo) + TObjectPtr Data; + + +public: + USaveSlot(); + + + /** Returns this slot's thumbnail if any */ + UFUNCTION(BlueprintCallable, Category = SlotInfo) + UTexture2D* GetThumbnail() const; + + /** Captures a thumbnail for the current slot */ + bool CaptureThumbnail(const int32 Width = 640, const int32 Height = 360); + + + USaveSlotData* GetData() const + { + return Data; + } + + // Internal use only recommended + void AssignData(USaveSlotData* NewData) + { + Data = NewData; + } + + UFUNCTION(BlueprintPure, Category = SlotInfo) + int32 GetMaxIds() const; + + UFUNCTION(BlueprintPure, Category = SlotInfo) + bool IsValidId(int32 CheckedId) const; + + /** Internal Usage. Will be called when an screenshot is captured */ + void _SetThumbnailPath(const FString& Path); + + /** Internal Usage. Will be called to remove previous thumbnail */ + FString _GetThumbnailPath() + { + return ThumbnailPath; + } + +public: + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = SlotInfo) + bool SetId(int32 Id); + + UFUNCTION(BlueprintPure, BlueprintImplementableEvent, Category = SlotInfo) + int32 GetId() const; + +protected: + virtual bool OnSetId(int32 Id); + virtual int32 OnGetId() const; +}; diff --git a/Source/SaveExtension/Public/SlotData.h b/Source/SaveExtension/Public/SaveSlotData.h similarity index 75% rename from Source/SaveExtension/Public/SlotData.h rename to Source/SaveExtension/Public/SaveSlotData.h index 1cf8f1d..5a167f0 100644 --- a/Source/SaveExtension/Public/SlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -1,35 +1,34 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ISaveExtension.h" +#include "Serialization/LevelRecords.h" +#include "Serialization/Records.h" - #include -#include +#include #include +#include #include #include -#include "Serialization/Records.h" -#include "Serialization/LevelRecords.h" - -#include "SlotData.generated.h" +#include "SaveSlotData.generated.h" /** - * USaveData stores all information that can be accessible only while the game is loaded. + * USaveSlotData stores all information that can be accessible only while the game is loaded. * Works like a common SaveGame object * E.g: Items, Quests, Enemies, World Actors, AI, Physics */ -UCLASS(ClassGroup = SaveExtension, hideCategories = ("Activation", "Actor Tick", "Actor", "Input", "Rendering", "Replication", "Socket", "Thumbnail")) -class SAVEEXTENSION_API USlotData : public USaveGame +UCLASS(ClassGroup = SaveExtension, hideCategories = ("Activation", "Actor Tick", "Actor", "Input", + "Rendering", "Replication", "Socket", "Thumbnail")) +class SAVEEXTENSION_API USaveSlotData : public USaveGame { GENERATED_BODY() public: - - USlotData() : Super() {} + USaveSlotData() : Super() {} /** Full Name of the Map where this SlotData was saved */ diff --git a/Source/SaveExtension/Public/Serialization/LevelRecords.h b/Source/SaveExtension/Public/Serialization/LevelRecords.h index 4dbcf9a..b892aef 100644 --- a/Source/SaveExtension/Public/Serialization/LevelRecords.h +++ b/Source/SaveExtension/Public/Serialization/LevelRecords.h @@ -1,17 +1,19 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "LevelFilter.h" +#include "Records.h" + #include -#include #include +#include -#include "Records.h" -#include "LevelFilter.h" #include "LevelRecords.generated.h" + /** Represents a level in the world (streaming or persistent) */ USTRUCT() struct FLevelRecord : public FBaseRecord @@ -33,7 +35,10 @@ struct FLevelRecord : public FBaseRecord virtual bool Serialize(FArchive& Ar) override; - bool IsValid() const { return !Name.IsNone(); } + bool IsValid() const + { + return !Name.IsNone(); + } void CleanRecords(); }; diff --git a/Source/SaveExtension/Public/Serialization/MTTask.h b/Source/SaveExtension/Public/Serialization/MTTask.h index d29eeae..cf4c233 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask.h +++ b/Source/SaveExtension/Public/Serialization/MTTask.h @@ -1,33 +1,33 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ISaveExtension.h" +#include "LevelFilter.h" +#include "SavePreset.h" #include #include #include #include -#include "SavePreset.h" -#include "LevelFilter.h" - ///////////////////////////////////////////////////// // FSlotDataActorsTask // Async task to serialize actors from a level. -class FMTTask : public FNonAbandonableTask { +class FMTTask : public FNonAbandonableTask +{ protected: - /** Used only if Sync */ const UWorld* const World; - USlotData* SlotData; + USaveSlotData* SlotData; // Locally cached settings const FSELevelFilter& Filter; - FMTTask(const bool bIsloading, const UWorld* InWorld, USlotData* InSlotData, const FSELevelFilter& Filter) + FMTTask( + const bool bIsloading, const UWorld* InWorld, USaveSlotData* InSlotData, const FSELevelFilter& Filter) : World(InWorld) , SlotData(InSlotData) , Filter(Filter) diff --git a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h index 702c6d3..d3b0bf6 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h +++ b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h @@ -1,27 +1,25 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once +#include "MTTask.h" #include "MTTask_SerializeActors.h" +#include "SavePreset.h" +#include "Serialization/LevelRecords.h" +#include "Serialization/Records.h" -#include +#include #include +#include #include -#include - -#include "SavePreset.h" - -#include "MTTask.h" -#include "Serialization/Records.h" -#include "Serialization/LevelRecords.h" -class USlotData; +class USaveSlotData; /** Called when game has been saved * @param SlotInfo the saved slot. Null if save failed */ -DECLARE_DELEGATE_OneParam(FOnGameSaved, USlotInfo*); +DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); ///////////////////////////////////////////////////// @@ -42,9 +40,9 @@ class FMTTask_SerializeActors : public FMTTask public: - FMTTask_SerializeActors(const UWorld* World, USlotData* SlotData, - const TArray* const InLevelActors, const int32 InStartIndex, const int32 InNum, bool bStoreGameInstance, - FLevelRecord* InLevelRecord, const FSELevelFilter& Filter) + FMTTask_SerializeActors(const UWorld* World, USaveSlotData* SlotData, + const TArray* const InLevelActors, const int32 InStartIndex, const int32 InNum, + bool bStoreGameInstance, FLevelRecord* InLevelRecord, const FSELevelFilter& Filter) : FMTTask(false, World, SlotData, Filter) , LevelActors(InLevelActors) , StartIndex(InStartIndex) @@ -61,7 +59,8 @@ class FMTTask_SerializeActors : public FMTTask void DoWork(); /** Called after task has completed to recover resulting information */ - void DumpData() { + void DumpData() + { if (LevelScriptRecord.IsValid()) LevelRecord->LevelScript = LevelScriptRecord; @@ -75,12 +74,12 @@ class FMTTask_SerializeActors : public FMTTask } private: - void SerializeGameInstance(); /** Serializes an actor into this Actor Record */ bool SerializeActor(const AActor* Actor, FActorRecord& Record) const; /** Serializes the components of an actor into a provided Actor Record */ - inline void SerializeActorComponents(const AActor* Actor, FActorRecord& ActorRecord, int8 indent = 0) const; + inline void SerializeActorComponents( + const AActor* Actor, FActorRecord& ActorRecord, int8 indent = 0) const; }; diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index c41c40b..a0fdfdd 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -1,15 +1,16 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include -#include #include +#include #include "Records.generated.h" -class USlotData; + +class USaveSlotData; USTRUCT() @@ -31,11 +32,19 @@ struct FBaseRecord virtual ~FBaseRecord() {} }; -template<> +template <> struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 -{ enum { WithSerializer = true }; }; +{ + enum + { + WithSerializer = true + }; +}; -FORCEINLINE bool operator==(const FBaseRecord& A, const FBaseRecord& B) { return A.Name == B.Name; } +FORCEINLINE bool operator==(const FBaseRecord& A, const FBaseRecord& B) +{ + return A.Name == B.Name; +} /** Represents a serialized Object */ @@ -61,7 +70,7 @@ struct FObjectRecord : public FBaseRecord return !Name.IsNone() && Class && Data.Num() > 0; } - FORCEINLINE bool operator== (const UObject* Other) const + FORCEINLINE bool operator==(const UObject* Other) const { return Other && Name == Other->GetFName() && Class == Other->GetClass(); } diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask.h b/Source/SaveExtension/Public/Serialization/SlotDataTask.h index 387ab59..a06a4f8 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask.h @@ -1,39 +1,37 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ISaveExtension.h" +#include "LevelFilter.h" +#include "SaveSlotData.h" #include #include #include -#include "SlotData.h" -#include "LevelFilter.h" - #include "SlotDataTask.generated.h" + class USaveManager; /** -* Base class for managing a SaveData file -*/ + * Base class for managing a SaveData file + */ UCLASS() -class USlotDataTask : public UObject +class USaveSlotDataTask : public UObject { GENERATED_BODY() private: - uint8 bRunning : 1; uint8 bFinished : 1; uint8 bSucceeded : 1; protected: - UPROPERTY() - USlotData* SlotData; + USaveSlotData* SlotData; UPROPERTY() const USavePreset* Preset; @@ -42,32 +40,42 @@ class USlotDataTask : public UObject float MaxFrameMs = 0.f; public: + USaveSlotDataTask() : Super(), bRunning(false), bFinished(false) {} - USlotDataTask() : Super(), bRunning(false), bFinished(false) {} - - void Prepare(USlotData* InSaveData, const USavePreset& InPreset) + void Prepare(USaveSlotData* InSaveData, const USavePreset& InPreset) { SlotData = InSaveData; Preset = &InPreset; MaxFrameMs = Preset->GetMaxFrameMs(); } - USlotDataTask* Start(); + USaveSlotDataTask* Start(); virtual void Tick(float DeltaTime) {} void Finish(bool bSuccess); - bool IsRunning() const { return bRunning; } - bool IsFinished() const { return bFinished; } - bool IsSucceeded() const { return IsFinished() && bSucceeded; } - bool IsFailed() const { return IsFinished() && !bSucceeded; } + bool IsRunning() const + { + return bRunning; + } + bool IsFinished() const + { + return bFinished; + } + bool IsSucceeded() const + { + return IsFinished() && bSucceeded; + } + bool IsFailed() const + { + return IsFinished() && !bSucceeded; + } bool IsScheduled() const; virtual void OnTick(float DeltaTime) {} protected: - virtual void OnStart() {} virtual void OnFinish(bool bSuccess) {} @@ -85,7 +93,8 @@ class USlotDataTask : public UObject virtual UWorld* GetWorld() const override; //~ End UObject Interface - FORCEINLINE float GetTimeMilliseconds() const { + FORCEINLINE float GetTimeMilliseconds() const + { return FPlatformTime::ToMilliseconds(FPlatformTime::Cycles()); } }; @@ -94,22 +103,23 @@ class USlotDataTask : public UObject ///////////////////////////////////////////////////// // FSlotDataActorsTask // Async task to serialize actors from a level. -class FSlotDataActorsTask : public FNonAbandonableTask { +class FSlotDataActorsTask : public FNonAbandonableTask +{ protected: - const bool bIsSync; /** USE ONLY IF SYNC */ const UWorld* const World; /** USE ONLY IF SYNC */ - USlotData* SlotData; + USaveSlotData* SlotData; const FSELevelFilter& Filter; - FSlotDataActorsTask(const bool bInIsSync, const UWorld* InWorld, USlotData* InSlotData, const FSELevelFilter& Filter) : - bIsSync(bInIsSync), - World(InWorld), - SlotData(InSlotData), - Filter(Filter) + FSlotDataActorsTask( + const bool bInIsSync, const UWorld* InWorld, USaveSlotData* InSlotData, const FSELevelFilter& Filter) + : bIsSync(bInIsSync) + , World(InWorld) + , SlotData(InSlotData) + , Filter(Filter) {} }; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h index 17d0bd1..ad95e3c 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h @@ -1,20 +1,19 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ISaveExtension.h" - #include "SavePreset.h" - #include "SlotDataTask_Loader.h" + #include "SlotDataTask_LevelLoader.generated.h" /** -* Manages the serializing process of a single level -*/ + * Manages the serializing process of a single level + */ UCLASS() -class USlotDataTask_LevelLoader : public USlotDataTask_Loader +class USaveSlotDataTask_LevelLoader : public USaveSlotDataTask_Loader { GENERATED_BODY() @@ -23,7 +22,6 @@ class USlotDataTask_LevelLoader : public USlotDataTask_Loader ULevelStreaming* StreamingLevel; public: - auto Setup(ULevelStreaming* InStreamingLevel) { StreamingLevel = InStreamingLevel; @@ -31,7 +29,6 @@ class USlotDataTask_LevelLoader : public USlotDataTask_Loader } private: - virtual void OnStart() override; virtual void DeserializeASyncLoop(float StartMS = 0.0f) override; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h index 63f1c88..bd792f9 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h @@ -1,20 +1,19 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include "ISaveExtension.h" - #include "SavePreset.h" - #include "SlotDataTask_Saver.h" + #include "SlotDataTask_LevelSaver.generated.h" /** -* Manages the serializing process of a single level -*/ + * Manages the serializing process of a single level + */ UCLASS() -class USlotDataTask_LevelSaver : public USlotDataTask_Saver +class USaveSlotDataTask_LevelSaver : public USaveSlotDataTask_Saver { GENERATED_BODY() @@ -23,7 +22,6 @@ class USlotDataTask_LevelSaver : public USlotDataTask_Saver ULevelStreaming* StreamingLevel; public: - auto Setup(ULevelStreaming* InStreamingLevel) { StreamingLevel = InStreamingLevel; @@ -31,9 +29,9 @@ class USlotDataTask_LevelSaver : public USlotDataTask_Saver } private: - virtual void OnStart() override; - virtual void OnFinish(bool bSuccess) override { + virtual void OnFinish(bool bSuccess) override + { SELog(Preset, "Finished Serializing level", FColor::Green); } }; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h index 7e5e911..8e6b99a 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -6,14 +6,14 @@ #include "ISaveExtension.h" #include "Multithreading/LoadFileTask.h" #include "SavePreset.h" -#include "SlotInfo.h" -#include "SlotData.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" #include "SlotDataTask.h" #include +#include #include #include -#include #include #include "SlotDataTask_Loader.generated.h" @@ -32,22 +32,21 @@ enum class ELoadDataTaskState : uint8 }; /** -* Manages the loading process of a SaveData file -*/ + * Manages the loading process of a SaveData file + */ UCLASS() -class USlotDataTask_Loader : public USlotDataTask +class USaveSlotDataTask_Loader : public USaveSlotDataTask { GENERATED_BODY() FName SlotName; UPROPERTY() - USlotInfo* NewSlotInfo; + USaveSlot* NewSlotInfo; FOnGameLoaded Delegate; protected: - // Async variables TWeakObjectPtr CurrentLevel; TWeakObjectPtr CurrentSLevel; @@ -63,8 +62,7 @@ class USlotDataTask_Loader : public USlotDataTask public: - - USlotDataTask_Loader() : Super() {} + USaveSlotDataTask_Loader() : Super() {} auto Setup(FName InSlotName) { @@ -72,12 +70,15 @@ class USlotDataTask_Loader : public USlotDataTask return this; } - auto Bind(const FOnGameLoaded& OnLoaded) { Delegate = OnLoaded; return this; } + auto Bind(const FOnGameLoaded& OnLoaded) + { + Delegate = OnLoaded; + return this; + } void OnMapLoaded(); private: - virtual void OnStart() override; virtual void Tick(float DeltaTime) override; @@ -90,12 +91,14 @@ class USlotDataTask_Loader : public USlotDataTask void RespawnActors(const TArray& Records, const ULevel* Level); protected: - //~ Begin Files void StartLoadingData(); - USlotData* GetLoadedData() const; - FORCEINLINE const bool IsDataLoaded() const { return LoadDataTask && LoadDataTask->IsDone(); }; + USaveSlotData* GetLoadedData() const; + FORCEINLINE const bool IsDataLoaded() const + { + return LoadDataTask && LoadDataTask->IsDone(); + }; //~ End Files @@ -115,12 +118,12 @@ class USlotDataTask_Loader : public USlotDataTask void PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord); /** Deserializes all Level actors. */ - inline void DeserializeLevel_Actor(AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter); + inline void DeserializeLevel_Actor( + AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter); void FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const; private: - /** Deserializes Game Instance Object and its Properties. Requires 'SaveGameMode' flag to be used. */ void DeserializeGameInstance(); @@ -129,6 +132,7 @@ class USlotDataTask_Loader : public USlotDataTask bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter); /** Deserializes the components of an actor from a provided Record */ - void DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 indent = 0); + void DeserializeActorComponents( + AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 indent = 0); /** END Deserialization */ }; \ No newline at end of file diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h index 46fb9b0..a11a368 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once @@ -7,25 +7,25 @@ #include "MTTask_SerializeActors.h" #include "Multithreading/SaveFileTask.h" #include "SavePreset.h" -#include "SlotData.h" +#include "SaveSlotData.h" #include "SlotDataTask.h" +#include +#include #include +#include #include #include -#include #include -#include -#include #include "SlotDataTask_Saver.generated.h" /** -* Manages the saving process of a SaveData file -*/ + * Manages the saving process of a SaveData file + */ UCLASS() -class USlotDataTask_Saver : public USlotDataTask +class USaveSlotDataTask_Saver : public USaveSlotDataTask { GENERATED_BODY() @@ -38,9 +38,8 @@ class USlotDataTask_Saver : public USlotDataTask FOnGameSaved Delegate; protected: - UPROPERTY() - USlotInfo* SlotInfo; + USaveSlot* SlotInfo; /** Start Async variables */ TWeakObjectPtr CurrentLevel; @@ -56,13 +55,10 @@ class USlotDataTask_Saver : public USlotDataTask public: + USaveSlotDataTask_Saver() : USaveSlotDataTask(), SaveTask(nullptr) {} - USlotDataTask_Saver() - : USlotDataTask() - , SaveTask(nullptr) - {} - - auto* Setup(FName InSlotName, bool bInOverride, bool bInSaveThumbnail, const int32 InWidth, const int32 InHeight) + auto* Setup( + FName InSlotName, bool bInOverride, bool bInSaveThumbnail, const int32 InWidth, const int32 InHeight) { SlotName = InSlotName; bOverride = bInOverride; @@ -73,7 +69,11 @@ class USlotDataTask_Saver : public USlotDataTask return this; } - auto* Bind(const FOnGameSaved& OnSaved) { Delegate = OnSaved; return this; } + auto* Bind(const FOnGameSaved& OnSaved) + { + Delegate = OnSaved; + return this; + } // Where all magic happens virtual void OnStart() override; @@ -82,21 +82,20 @@ class USlotDataTask_Saver : public USlotDataTask virtual void BeginDestroy() override; protected: - /** BEGIN Serialization */ /** Serializes all world actors. */ void SerializeWorld(); void PrepareAllLevels(const TArray& Levels); - void SerializeLevelSync(const ULevel* Level, int32 AssignedThreads, const ULevelStreaming* StreamingLevel = nullptr); + void SerializeLevelSync( + const ULevel* Level, int32 AssignedThreads, const ULevelStreaming* StreamingLevel = nullptr); /** END Serialization */ void RunScheduledTasks(); private: - /** BEGIN FileSaving */ void SaveFile(); /** End FileSaving */ diff --git a/Source/SaveExtension/Public/SlotInfo.h b/Source/SaveExtension/Public/SlotInfo.h deleted file mode 100644 index 115b18b..0000000 --- a/Source/SaveExtension/Public/SlotInfo.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once - -#include -#include -#include - -#include "SlotInfo.generated.h" - - -/** - * USaveInfo stores information that needs to be accessible WITHOUT loading the game. - * Works like a common SaveGame object - * E.g: Dates, played time, progress, level - */ -UCLASS(ClassGroup = SaveExtension, hideCategories = ("Activation", "Actor Tick", "Actor", "Input", "Rendering", "Replication", "Socket", "Thumbnail")) -class SAVEEXTENSION_API USlotInfo : public USaveGame -{ - GENERATED_BODY() - -public: - - USlotInfo() : Super() - , PlayedTime(FTimespan::Zero()) - , SlotPlayedTime(FTimespan::Zero()) - , SaveDate(FDateTime::Now()) - {} - - - /** Slot where this SaveInfo and its saveData are saved */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) - FName FileName; - - UPROPERTY(BlueprintReadWrite, Category = SlotInfo) - FText DisplayName; - - /** Played time since this saved game was started. Not related to slots, slots can change */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) - FTimespan PlayedTime; - - /** Played time since this saved game was created */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) - FTimespan SlotPlayedTime; - - /** Last date this slot was saved. */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) - FDateTime SaveDate; - - /** Date at which this slot was loaded. */ - UPROPERTY(BlueprintReadOnly, Transient, Category = SlotInfo) - FDateTime LoadDate; - - /** Root Level where this Slot was saved */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) - FName Map; - -private: - - UPROPERTY() - FString ThumbnailPath; - - /** Thumbnail gets cached here the first time it is requested */ - UPROPERTY(Transient) - UTexture2D* CachedThumbnail; - - -public: - - /** Returns this slot's thumbnail if any */ - UFUNCTION(BlueprintCallable, Category = SlotInfo) - UTexture2D* GetThumbnail() const; - - /** Captures a thumbnail for the current slot */ - bool CaptureThumbnail(const int32 Width = 640, const int32 Height = 360); - - - /** Internal Usage. Will be called when an screenshot is captured */ - void _SetThumbnailPath(const FString& Path); - - /** Internal Usage. Will be called to remove previous thumbnail */ - FString _GetThumbnailPath() { return ThumbnailPath; } -}; diff --git a/Source/SaveExtension/SaveExtension.Build.cs b/Source/SaveExtension/SaveExtension.Build.cs index c09e149..891befc 100644 --- a/Source/SaveExtension/SaveExtension.Build.cs +++ b/Source/SaveExtension/SaveExtension.Build.cs @@ -3,17 +3,18 @@ using UnrealBuildTool; using System.IO; -namespace UnrealBuildTool.Rules { - -public class SaveExtension : ModuleRules +namespace UnrealBuildTool.Rules { - public SaveExtension(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - bEnforceIWYU = true; - PublicDependencyModuleNames.AddRange(new string[] + public class SaveExtension : ModuleRules + { + public SaveExtension(ReadOnlyTargetRules Target) : base(Target) { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + IWYUSupport = IWYUSupport.Full; + + PublicDependencyModuleNames.AddRange(new string[] + { "Core", "Engine", "Foliage", @@ -22,8 +23,8 @@ public SaveExtension(ReadOnlyTargetRules Target) : base(Target) "DeveloperSettings", "ImageWrapper", "NavigationSystem" - }); + }); + } } -} } diff --git a/Source/Test/Private/Files.spec.cpp b/Source/Test/Private/Files.spec.cpp index 6218601..7094c90 100644 --- a/Source/Test/Private/Files.spec.cpp +++ b/Source/Test/Private/Files.spec.cpp @@ -1,9 +1,9 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Automatron.h" +#include "FileAdapter.h" #include "Helpers/TestActor.h" #include "SaveManager.h" -#include "FileAdapter.h" class FSaveSpec_Files : public Automatron::FTestSpec @@ -44,22 +44,23 @@ void FSaveSpec_Files::Define() TestTrue("Saved", SaveManager->SaveSlot(0)); - TestTrue("Info File exists in disk", FFileAdapter::DoesFileExist(TEXT("0"))); + TestTrue("Info File exists in disk", FFileAdapter::FileExists(TEXT("0"))); }); It("Can save files asynchronously", [this]() { TestPreset->MultithreadedFiles = ESaveASyncMode::SaveAsync; bFinishTick = false; - bool bSaving = SaveManager->SaveSlot(0, true, false, {}, FOnGameSaved::CreateLambda([this](auto* Info) { - // Notified that files have been saved asynchronously - TestTrue("Info File exists in disk", FFileAdapter::DoesFileExist(TEXT("0"))); - bFinishTick = true; - })); + bool bSaving = + SaveManager->SaveSlot(0, true, false, {}, FOnGameSaved::CreateLambda([this](auto* Info) { + // Notified that files have been saved asynchronously + TestTrue("Info File exists in disk", FFileAdapter::FileExists(TEXT("0"))); + bFinishTick = true; + })); TestTrue("Started Saving", bSaving); // Files shouldn't exist yet - TestFalse("Info File exists in disk", FFileAdapter::DoesFileExist(TEXT("0"))); + TestFalse("Info File exists in disk", FFileAdapter::FileExists(TEXT("0"))); TickWorldUntil(GetMainWorld(), true, [this](float) { return !bFinishTick; @@ -71,11 +72,10 @@ void FSaveSpec_Files::Define() TestTrue("Saved", SaveManager->SaveSlot(0)); - USlotInfo* Info = nullptr; - USlotData* Data = nullptr; - TestTrue("File was loaded", FFileAdapter::LoadFile(TEXT("0"), Info, Data, true, SaveManager)); - TestNotNull("Info is valid", Info); - TestNotNull("Data is valid", Data); + USaveSlot* Slot = nullptr; + TestNotNull("File was loaded", FFileAdapter::LoadFile(TEXT("0"), Slot, true, SaveManager)); + TestNotNull("Info is valid", Slot); + TestNotNull("Data is valid", Slot->GetData()); }); AfterEach([this]() { diff --git a/Source/Test/Private/GameInstance.spec.cpp b/Source/Test/Private/GameInstance.spec.cpp index 0b72cfd..eed461d 100644 --- a/Source/Test/Private/GameInstance.spec.cpp +++ b/Source/Test/Private/GameInstance.spec.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Automatron.h" #include "Helpers/TestGameInstance.h" @@ -41,8 +41,7 @@ void FSaveSpec_GameInstance::Define() TestPreset->MultithreadedSerialization = ESaveASyncMode::OnlySync; }); - It("GameInstance can be saved", [this]() - { + It("GameInstance can be saved", [this]() { auto* GI = GetMainWorld()->GetGameInstance(); GI->bMyBool = true; @@ -56,13 +55,11 @@ void FSaveSpec_GameInstance::Define() TestTrue("Saved variable loaded", GI->bMyBool); }); - AfterEach([this]() - { + AfterEach([this]() { if (SaveManager) { bFinishTick = false; - SaveManager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() - { + SaveManager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() { bFinishTick = true; })); diff --git a/Source/Test/Private/Helpers/TestActor.h b/Source/Test/Private/Helpers/TestActor.h index 610d7f6..f380d13 100644 --- a/Source/Test/Private/Helpers/TestActor.h +++ b/Source/Test/Private/Helpers/TestActor.h @@ -1,61 +1,62 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include #include + #include "TestActor.generated.h" + USTRUCT() struct FTestSaveStruct { - GENERATED_BODY() + GENERATED_BODY() }; UCLASS() class ATestActor : public AActor { - GENERATED_BODY() + GENERATED_BODY() public: + UPROPERTY(SaveGame) + bool bMyBool = false; - UPROPERTY(SaveGame) - bool bMyBool = false; - - UPROPERTY(SaveGame) - float MyFloat = 0.f; + UPROPERTY(SaveGame) + float MyFloat = 0.f; - // INTEGERS + // INTEGERS - UPROPERTY(SaveGame) - uint8 MyU8 = 0.f; + UPROPERTY(SaveGame) + uint8 MyU8 = 0.f; - UPROPERTY(SaveGame) - uint16 MyU16 = 0; + UPROPERTY(SaveGame) + uint16 MyU16 = 0; - UPROPERTY(SaveGame) - uint32 MyU32 = 0; + UPROPERTY(SaveGame) + uint32 MyU32 = 0; - UPROPERTY(SaveGame) - uint64 MyU64 = 0; + UPROPERTY(SaveGame) + uint64 MyU64 = 0; - UPROPERTY(SaveGame) - int8 MyI8 = 0.f; + UPROPERTY(SaveGame) + int8 MyI8 = 0.f; - UPROPERTY(SaveGame) - int16 MyI16 = 0; + UPROPERTY(SaveGame) + int16 MyI16 = 0; - UPROPERTY(SaveGame) - int32 MyI32 = 0; + UPROPERTY(SaveGame) + int32 MyI32 = 0; - UPROPERTY(SaveGame) - int64 MyI64 = 0; + UPROPERTY(SaveGame) + int64 MyI64 = 0; - UPROPERTY(SaveGame) - FTestSaveStruct MyStruct; + UPROPERTY(SaveGame) + FTestSaveStruct MyStruct; }; diff --git a/Source/Test/Private/Helpers/TestGameInstance.h b/Source/Test/Private/Helpers/TestGameInstance.h index 7ede425..dfb6145 100644 --- a/Source/Test/Private/Helpers/TestGameInstance.h +++ b/Source/Test/Private/Helpers/TestGameInstance.h @@ -1,19 +1,20 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once #include #include + #include "TestGameInstance.generated.h" + UCLASS() class UTestGameInstance : public UGameInstance { - GENERATED_BODY() + GENERATED_BODY() public: - - UPROPERTY(SaveGame) - bool bMyBool = false; + UPROPERTY(SaveGame) + bool bMyBool = false; }; diff --git a/Source/Test/Private/Save.spec.cpp b/Source/Test/Private/Save.spec.cpp index 3da63a9..b55fcc4 100644 --- a/Source/Test/Private/Save.spec.cpp +++ b/Source/Test/Private/Save.spec.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "Automatron.h" #include "Helpers/TestActor.h" @@ -82,8 +82,7 @@ void FSaveSpec_Preset::Define() TestPreset->MultithreadedSerialization = ESaveASyncMode::OnlySync; }); - It("bool", [this]() - { + It("bool", [this]() { TestActor->bMyBool = true; SaveManager->SaveSlot(0); TestTrue("bool didn't change after save", TestActor->bMyBool); @@ -95,8 +94,7 @@ void FSaveSpec_Preset::Define() TestTrue("bool was saved", TestActor->bMyBool); }); - It("uint8", [this]() - { + It("uint8", [this]() { TestActor->MyU8 = 34; SaveManager->SaveSlot(0); TestEqual("uint8 didn't change after save", TestActor->MyU8, 34); @@ -106,8 +104,7 @@ void FSaveSpec_Preset::Define() TestEqual("uint8 was saved", TestActor->MyU8, 34); }); - It("uint16", [this]() - { + It("uint16", [this]() { TestActor->MyU16 = 34; SaveManager->SaveSlot(0); TestEqual("uint16 didn't change after save", TestActor->MyU16, 34); @@ -117,8 +114,7 @@ void FSaveSpec_Preset::Define() TestEqual("uint16 was saved", TestActor->MyU16, 34); }); - It("uint32", [this]() - { + It("uint32", [this]() { TestActor->MyU32 = 34; SaveManager->SaveSlot(0); TestEqual("uint32 didn't change after save", TestActor->MyU32, 34); @@ -128,8 +124,7 @@ void FSaveSpec_Preset::Define() TestEqual("uint32 was saved", TestActor->MyU32, 34); }); - It("uint64", [this]() - { + It("uint64", [this]() { TestActor->MyU64 = 34; SaveManager->SaveSlot(0); TestEqual("uint16 didn't change after save", TestActor->MyU64, 34); @@ -139,8 +134,7 @@ void FSaveSpec_Preset::Define() TestEqual("uint16 was saved", TestActor->MyU64, 34); }); - It("int8", [this]() - { + It("int8", [this]() { TestActor->MyI8 = 34; SaveManager->SaveSlot(0); TestEqual("int8 didn't change after save", TestActor->MyI8, 34); @@ -150,8 +144,7 @@ void FSaveSpec_Preset::Define() TestEqual("int8 was saved", TestActor->MyI8, 34); }); - It("int16", [this]() - { + It("int16", [this]() { TestActor->MyI16 = 34; SaveManager->SaveSlot(0); TestEqual("int16 didn't change after save", TestActor->MyI16, 34); @@ -161,8 +154,7 @@ void FSaveSpec_Preset::Define() TestEqual("int16 was saved", TestActor->MyI16, 34); }); - It("int32", [this]() - { + It("int32", [this]() { TestActor->MyI32 = 34; SaveManager->SaveSlot(0); TestEqual("int32 didn't change after save", TestActor->MyI32, 34); @@ -172,8 +164,7 @@ void FSaveSpec_Preset::Define() TestEqual("int32 was saved", TestActor->MyI32, 34); }); - It("int64", [this]() - { + It("int64", [this]() { TestActor->MyI64 = 34; SaveManager->SaveSlot(0); TestEqual("int64 didn't change after save", TestActor->MyI64, 34); diff --git a/Source/Test/Private/SaveExtensionTest.cpp b/Source/Test/Private/SaveExtensionTest.cpp index 69ff67e..ce019a9 100644 --- a/Source/Test/Private/SaveExtensionTest.cpp +++ b/Source/Test/Private/SaveExtensionTest.cpp @@ -1,9 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #include "SaveExtensionTest.h" + #include "Automatron.h" + IMPLEMENT_MODULE(FSaveExtensionTest, SaveExtensionTest); void FSaveExtensionTest::StartupModule() diff --git a/Source/Test/Public/SaveExtensionTest.h b/Source/Test/Public/SaveExtensionTest.h index e13d993..4c347c2 100644 --- a/Source/Test/Public/SaveExtensionTest.h +++ b/Source/Test/Public/SaveExtensionTest.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once diff --git a/Source/Test/SaveExtensionTest.Build.cs b/Source/Test/SaveExtensionTest.Build.cs index 5281610..5297223 100644 --- a/Source/Test/SaveExtensionTest.Build.cs +++ b/Source/Test/SaveExtensionTest.Build.cs @@ -5,11 +5,12 @@ namespace UnrealBuildTool.Rules { - public class SaveExtensionTest : ModuleRules { + public class SaveExtensionTest : ModuleRules + { public SaveExtensionTest(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - bEnforceIWYU = true; + IWYUSupport = IWYUSupport.Full; PublicDependencyModuleNames.AddRange(new string[] { @@ -20,10 +21,10 @@ public SaveExtensionTest(ReadOnlyTargetRules Target) : base(Target) "EngineSettings" }); - if (Target.bBuildEditor == true) - { - PrivateDependencyModuleNames.Add("UnrealEd"); - } - } + if (Target.bBuildEditor == true) + { + PrivateDependencyModuleNames.Add("UnrealEd"); + } + } } } \ No newline at end of file From dbdc11921c04d8f92da58b0cd5a5cf7e9b5a1387 Mon Sep 17 00:00:00 2001 From: muit Date: Mon, 18 Sep 2023 16:58:47 +0200 Subject: [PATCH 2/7] Removed presets --- .../Asset/AssetTypeAction_SavePreset.cpp | 23 -- .../Asset/AssetTypeAction_SavePreset.h | 28 --- .../Asset/AssetTypeAction_SaveSlot.cpp | 2 +- .../Asset/AssetTypeAction_SaveSlotData.cpp | 2 +- .../Private/Asset/SavePresetFactory.cpp | 26 --- .../Editor/Private/Asset/SavePresetFactory.h | 23 -- .../SEActorClassFilterCustomization.cpp | 1 - .../SEComponentClassFilterCustomization.cpp | 1 - ...ePresetDetails.cpp => SaveSlotDetails.cpp} | 38 ++-- ...{SavePresetDetails.h => SaveSlotDetails.h} | 6 +- Source/Editor/Private/SaveExtensionEditor.cpp | 8 +- .../Private/LatentActions/LoadInfosAction.cpp | 10 +- Source/SaveExtension/Private/LevelFilter.cpp | 84 +++++++ .../Private/Misc/SlotHelpers.cpp | 3 +- .../Multithreading/DeleteSlotsTask.cpp | 10 +- .../Private/Multithreading/LoadFileTask.cpp | 2 - ...oadSlotInfosTask.cpp => LoadSlotsTask.cpp} | 15 +- .../Private/Multithreading/SaveFileTask.cpp | 9 - .../{FileAdapter.cpp => SaveFileHelpers.cpp} | 55 +++-- Source/SaveExtension/Private/SaveManager.cpp | 59 ++--- Source/SaveExtension/Private/SavePreset.cpp | 21 -- Source/SaveExtension/Private/SaveSlot.cpp | 89 ++++++-- Source/SaveExtension/Private/SaveSlotData.cpp | 4 +- .../Private/Serialization/LevelRecords.cpp | 4 +- .../Serialization/MTTask_SerializeActors.cpp | 2 - .../Private/Serialization/SlotDataTask.cpp | 15 +- .../SlotDataTask_LevelLoader.cpp | 2 +- .../Serialization/SlotDataTask_Loader.cpp | 53 +++-- .../Serialization/SlotDataTask_Saver.cpp | 53 +++-- Source/SaveExtension/Public/ISaveExtension.h | 13 +- .../Public/LatentActions/LoadInfosAction.h | 4 +- Source/SaveExtension/Public/LevelFilter.h | 109 +++------ .../SaveExtension/Public/Misc/SlotHelpers.h | 3 +- .../Public/Multithreading/Delegates.h | 2 +- .../Public/Multithreading/DeleteSlotsTask.h | 3 +- .../Public/Multithreading/LoadFileTask.h | 20 +- .../{LoadSlotInfosTask.h => LoadSlotsTask.h} | 16 +- .../Public/Multithreading/SaveFileTask.h | 4 +- .../{FileAdapter.h => SaveFileHelpers.h} | 11 +- Source/SaveExtension/Public/SaveManager.h | 102 +++++---- Source/SaveExtension/Public/SavePreset.h | 211 ------------------ Source/SaveExtension/Public/SaveSlot.h | 176 ++++++++++++--- Source/SaveExtension/Public/SaveSlotData.h | 3 +- .../Public/Serialization/LevelRecords.h | 5 +- .../Public/Serialization/MTTask.h | 1 - .../Serialization/MTTask_SerializeActors.h | 4 +- .../Public/Serialization/SlotDataTask.h | 12 +- .../Serialization/SlotDataTask_LevelLoader.h | 1 - .../Serialization/SlotDataTask_LevelSaver.h | 3 +- .../Serialization/SlotDataTask_Loader.h | 4 +- .../Public/Serialization/SlotDataTask_Saver.h | 3 +- Source/Test/Private/Files.spec.cpp | 24 +- ...Instance.spec.cpp => GameInstanceSpec.cpp} | 10 +- Source/Test/Private/GameInstanceSpec.h | 23 ++ .../Private/{Save.spec.cpp => SavingSpec.cpp} | 16 +- Source/Test/Private/SavingSpec.h | 24 ++ 56 files changed, 662 insertions(+), 793 deletions(-) delete mode 100644 Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp delete mode 100644 Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h delete mode 100644 Source/Editor/Private/Asset/SavePresetFactory.cpp delete mode 100644 Source/Editor/Private/Asset/SavePresetFactory.h rename Source/Editor/Private/Customizations/{SavePresetDetails.cpp => SaveSlotDetails.cpp} (59%) rename Source/Editor/Private/Customizations/{SavePresetDetails.h => SaveSlotDetails.h} (87%) rename Source/SaveExtension/Private/Multithreading/{LoadSlotInfosTask.cpp => LoadSlotsTask.cpp} (78%) delete mode 100644 Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp rename Source/SaveExtension/Private/{FileAdapter.cpp => SaveFileHelpers.cpp} (82%) delete mode 100644 Source/SaveExtension/Private/SavePreset.cpp rename Source/SaveExtension/Public/Multithreading/{LoadSlotInfosTask.h => LoadSlotsTask.h} (63%) rename Source/SaveExtension/Public/{FileAdapter.h => SaveFileHelpers.h} (88%) delete mode 100644 Source/SaveExtension/Public/SavePreset.h rename Source/Test/Private/{GameInstance.spec.cpp => GameInstanceSpec.cpp} (82%) create mode 100644 Source/Test/Private/GameInstanceSpec.h rename Source/Test/Private/{Save.spec.cpp => SavingSpec.cpp} (89%) create mode 100644 Source/Test/Private/SavingSpec.h diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp deleted file mode 100644 index ef30e22..0000000 --- a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "AssetTypeAction_SavePreset.h" - -#define LOCTEXT_NAMESPACE "AssetTypeActions" - - -////////////////////////////////////////////////////////////////////////// -// FAssetTypeAction_SavePreset - -FText FAssetTypeAction_SavePreset::GetName() const -{ - return LOCTEXT("FAssetTypeAction_SavePresetName", "Save Preset"); -} - -FColor FAssetTypeAction_SavePreset::GetTypeColor() const -{ - return FColor(63, 126, 255); -} - -////////////////////////////////////////////////////////////////////////// - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h b/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h deleted file mode 100644 index 5a446bb..0000000 --- a/Source/Editor/Private/Asset/AssetTypeAction_SavePreset.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "SaveExtensionEditor.h" - -#include -#include - - -class FAssetTypeAction_SavePreset : public FAssetTypeActions_Base -{ -public: - virtual uint32 GetCategories() override - { - return FSaveExtensionEditor::Get().AssetCategory; - } - - virtual FText GetName() const override; - virtual FColor GetTypeColor() const override; - - virtual UClass* GetSupportedClass() const override - { - return USavePreset::StaticClass(); - } -}; - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp index 679ce81..d5d235e 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////////// -// FAssetTypeAction_SavePreset +// FAssetTypeAction_SaveSlot FText FAssetTypeAction_SaveSlot::GetName() const { diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp index 4d226a9..828458d 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////////// -// FAssetTypeAction_SavePreset +// FAssetTypeAction_SaveSlotData FText FAssetTypeAction_SaveSlotData::GetName() const { diff --git a/Source/Editor/Private/Asset/SavePresetFactory.cpp b/Source/Editor/Private/Asset/SavePresetFactory.cpp deleted file mode 100644 index e09eb92..0000000 --- a/Source/Editor/Private/Asset/SavePresetFactory.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Asset/SavePresetFactory.h" - -#include - - -USavePresetFactory::USavePresetFactory() : Super() -{ - bCreateNew = true; - bEditAfterNew = true; - SupportedClass = USavePreset::StaticClass(); -} - -UObject* USavePresetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, - EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) -{ - check(Class->IsChildOf(USavePreset::StaticClass())); - - if (!FKismetEditorUtilities::CanCreateBlueprintOfClass(Class)) - { - return nullptr; - } - return FKismetEditorUtilities::CreateBlueprint(Class, InParent, Name, BPTYPE_Normal, - UBlueprint::StaticClass(), UBlueprintGeneratedClass::StaticClass(), TEXT("AssetTypeActions")); -} diff --git a/Source/Editor/Private/Asset/SavePresetFactory.h b/Source/Editor/Private/Asset/SavePresetFactory.h deleted file mode 100644 index 3870356..0000000 --- a/Source/Editor/Private/Asset/SavePresetFactory.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "AssetTypeActions_Base.h" -#include "Factories/Factory.h" -#include "SavePreset.h" - -#include "SavePresetFactory.generated.h" - - - -UCLASS() -class SAVEEXTENSIONEDITOR_API USavePresetFactory : public UFactory -{ - GENERATED_BODY() - -public: - USavePresetFactory(); - - virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, - UObject* Context, FFeedbackContext* Warn) override; -}; diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp index 9ee5f38..ac3f992 100644 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp @@ -12,7 +12,6 @@ TSharedPtr FSEActorClassFilterCustomization::GetFilterHandle( TSharedRef StructPropertyHandle) { return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEActorClassFilter, ClassFilter)); - ; } #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp index b675af3..c0e79af 100644 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp @@ -12,7 +12,6 @@ TSharedPtr FSEComponentClassFilterCustomization::GetFilterHandl TSharedRef StructPropertyHandle) { return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEComponentClassFilter, ClassFilter)); - ; } #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SavePresetDetails.cpp b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp similarity index 59% rename from Source/Editor/Private/Customizations/SavePresetDetails.cpp rename to Source/Editor/Private/Customizations/SaveSlotDetails.cpp index 4dc6dec..9ed50fb 100644 --- a/Source/Editor/Private/Customizations/SavePresetDetails.cpp +++ b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp @@ -1,36 +1,36 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "SavePresetDetails.h" +#include "SaveSlotDetails.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" -#include "SavePreset.h" #include +#include #include -#define LOCTEXT_NAMESPACE "FSavePresetDetails" +#define LOCTEXT_NAMESPACE "FSaveSlotDetails" /************************************************************************ - * FSavePresetDetails + * FSaveSlotDetails */ -TSharedRef FSavePresetDetails::MakeInstance() +TSharedRef FSaveSlotDetails::MakeInstance() { - return MakeShareable(new FSavePresetDetails); + return MakeShareable(new FSaveSlotDetails); } -void FSavePresetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) +void FSaveSlotDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) { TArray> Objects; DetailBuilder.GetObjectsBeingCustomized(Objects); if (Objects.Num() && Objects[0] != nullptr) { - Settings = CastChecked(Objects[0].Get()); + Slot = CastChecked(Objects[0].Get()); DetailBuilder.EditCategory(TEXT("Gameplay")); DetailBuilder.EditCategory(TEXT("Serialization")); @@ -40,14 +40,14 @@ void FSavePresetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) Category.AddProperty(TEXT("MultithreadedSerialization")); Category.AddProperty(TEXT("FrameSplittedSerialization")); Category.AddCustomRow(LOCTEXT("AsyncWarning", "Asynchronous Warning")) - .Visibility({this, &FSavePresetDetails::GetWarningVisibility}) + .Visibility({this, &FSaveSlotDetails::GetWarningVisibility}) .ValueContent() .MinDesiredWidth(300.f) .MaxDesiredWidth( 400.f)[SNew(SBorder) .Padding(2.f) .BorderImage(FAppStyle::GetBrush("ErrorReporting.EmptyBox")) - .BorderBackgroundColor(this, &FSavePresetDetails::GetWarningColor) + .BorderBackgroundColor(this, &FSaveSlotDetails::GetWarningColor) [SNew(STextBlock) .Text(LOCTEXT("AsyncWarningText", "WARNING: Frame-splitted loading or saving is not recommended " @@ -55,33 +55,33 @@ void FSavePresetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) .AutoWrapText(true)]]; Category.AddProperty(TEXT("MaxFrameMs")) - .EditCondition({this, &FSavePresetDetails::CanEditAsynchronous}, nullptr); + .EditCondition({this, &FSaveSlotDetails::CanEditAsynchronous}, nullptr); } DetailBuilder.EditCategory(TEXT("Level Streaming")); } } -FSlateColor FSavePresetDetails::GetWarningColor() const +FSlateColor FSaveSlotDetails::GetWarningColor() const { return FLinearColor{FColor{234, 220, 25, 128}}; } -EVisibility FSavePresetDetails::GetWarningVisibility() const +EVisibility FSaveSlotDetails::GetWarningVisibility() const { - if (Settings.IsValid()) + if (Slot.IsValid()) { - return Settings->GetFrameSplitSerialization() == ESaveASyncMode::OnlySync ? EVisibility::Collapsed - : EVisibility::Visible; + return Slot->GetFrameSplitSerialization() == ESaveASyncMode::OnlySync ? EVisibility::Collapsed + : EVisibility::Visible; } return EVisibility::Collapsed; } -bool FSavePresetDetails::CanEditAsynchronous() const +bool FSaveSlotDetails::CanEditAsynchronous() const { - if (Settings.IsValid()) + if (Slot.IsValid()) { - return Settings->GetFrameSplitSerialization() != ESaveASyncMode::OnlySync; + return Slot->GetFrameSplitSerialization() != ESaveASyncMode::OnlySync; } return true; } diff --git a/Source/Editor/Private/Customizations/SavePresetDetails.h b/Source/Editor/Private/Customizations/SaveSlotDetails.h similarity index 87% rename from Source/Editor/Private/Customizations/SavePresetDetails.h rename to Source/Editor/Private/Customizations/SaveSlotDetails.h index 13ef8a9..65ec884 100644 --- a/Source/Editor/Private/Customizations/SavePresetDetails.h +++ b/Source/Editor/Private/Customizations/SaveSlotDetails.h @@ -14,10 +14,10 @@ class IDetailsView; class IDetailLayoutBuilder; -class USavePreset; +class USaveSlot; -class FSavePresetDetails : public IDetailCustomization +class FSaveSlotDetails : public IDetailCustomization { public: /** Makes a new instance of this detail layout class for a specific detail view requesting it */ @@ -32,5 +32,5 @@ class FSavePresetDetails : public IDetailCustomization bool CanEditAsynchronous() const; - TWeakObjectPtr Settings; + TWeakObjectPtr Slot; }; diff --git a/Source/Editor/Private/SaveExtensionEditor.cpp b/Source/Editor/Private/SaveExtensionEditor.cpp index 29c89db..cf68f32 100644 --- a/Source/Editor/Private/SaveExtensionEditor.cpp +++ b/Source/Editor/Private/SaveExtensionEditor.cpp @@ -2,14 +2,13 @@ #include "SaveExtensionEditor.h" -#include "Asset/AssetTypeAction_SavePreset.h" #include "Asset/AssetTypeAction_SaveSlot.h" #include "Asset/AssetTypeAction_SaveSlotData.h" #include "Customizations/SEActorClassFilterCustomization.h" #include "Customizations/SEClassFilterCustomization.h" #include "Customizations/SEClassFilterGraphPanelPinFactory.h" #include "Customizations/SEComponentClassFilterCustomization.h" -#include "Customizations/SavePresetDetails.h" +#include "Customizations/SaveSlotDetails.h" #include "Kismet2/KismetEditorUtilities.h" @@ -24,7 +23,6 @@ void FSaveExtensionEditor::StartupModule() AssetTools.RegisterAssetTypeActions(MakeShared()); AssetTools.RegisterAssetTypeActions(MakeShared()); - AssetTools.RegisterAssetTypeActions(MakeShared()); RegisterPropertyTypeCustomizations(); @@ -46,7 +44,7 @@ void FSaveExtensionEditor::ShutdownModule() void FSaveExtensionEditor::RegisterPropertyTypeCustomizations() { RegisterCustomClassLayout( - "SavePreset", FOnGetDetailCustomizationInstance::CreateStatic(&FSavePresetDetails::MakeInstance)); + "SaveSlot", FOnGetDetailCustomizationInstance::CreateStatic(&FSaveSlotDetails::MakeInstance)); RegisterCustomPropertyTypeLayout("SEClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); @@ -56,8 +54,6 @@ void FSaveExtensionEditor::RegisterPropertyTypeCustomizations() RegisterCustomPropertyTypeLayout( "SEComponentClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( &FSEComponentClassFilterCustomization::MakeInstance)); - // RegisterCustomPropertyTypeLayout("SavePreset", - // FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSavePresetCustomization::MakeInstance)); RegisterCustomPinFactory(); } diff --git a/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp b/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp index e8ada39..4b989e8 100644 --- a/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp +++ b/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp @@ -7,17 +7,17 @@ FLoadInfosAction::FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, - TArray& InSlotInfos, ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo) + TArray& InSlots, ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo) : Result(OutResult) - , SlotInfos(InSlotInfos) + , Slots(InSlots) , bFinished(false) , ExecutionFunction(LatentInfo.ExecutionFunction) , OutputLink(LatentInfo.Linkage) , CallbackTarget(LatentInfo.CallbackTarget) { - Manager->LoadAllSlotInfos( - bSortByRecent, FOnSlotInfosLoaded::CreateLambda([this](const TArray& Results) { - SlotInfos = Results; + Manager->FindAllSlots( + bSortByRecent, FOnSlotsLoaded::CreateLambda([this](const TArray& Results) { + Slots = Results; bFinished = true; })); } diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index e6c36c0..e7247c5 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -2,6 +2,8 @@ #include "LevelFilter.h" +#include "SaveSlot.h" + ///////////////////////////////////////////////////// // USaveDataTask @@ -10,3 +12,85 @@ const FName FSELevelFilter::TagNoTransform{"!SaveTransform"}; const FName FSELevelFilter::TagNoPhysics{"!SavePhysics"}; const FName FSELevelFilter::TagNoTags{"!SaveTags"}; const FName FSELevelFilter::TagTransform{"SaveTransform"}; + +void FSELevelFilter::FromSlot(const USaveSlot& Slot) +{ + ActorFilter = Slot.GetActorFilter(true); + LoadActorFilter = Slot.GetActorFilter(false); + bStoreComponents = Slot.bStoreComponents; + ComponentFilter = Slot.GetComponentFilter(true); + LoadComponentFilter = Slot.GetComponentFilter(false); +} + +void FSELevelFilter::BakeAllowedClasses() const +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FSELevelFilter::BakeAllowedClasses); + ActorFilter.BakeAllowedClasses(); + ComponentFilter.BakeAllowedClasses(); + LoadActorFilter.BakeAllowedClasses(); + LoadComponentFilter.BakeAllowedClasses(); +} + +bool FSELevelFilter::ShouldSave(const AActor* Actor) const +{ + return ActorFilter.IsClassAllowed(Actor->GetClass()); +} + +bool FSELevelFilter::ShouldSave(const UActorComponent* Component) const +{ + return IsValid(Component) && ComponentFilter.IsClassAllowed(Component->GetClass()); +} + +bool FSELevelFilter::ShouldLoad(const AActor* Actor) const +{ + return LoadActorFilter.IsClassAllowed(Actor->GetClass()); +} + +bool FSELevelFilter::ShouldLoad(const UActorComponent* Component) const +{ + return IsValid(Component) && LoadComponentFilter.IsClassAllowed(Component->GetClass()); +} + +bool FSELevelFilter::StoresTransform(const UActorComponent* Component) +{ + return Component->GetClass()->IsChildOf() && HasTag(Component, TagTransform); +} + +bool FSELevelFilter::StoresTags(const UActorComponent* Component) +{ + return !HasTag(Component, TagNoTags); +} + +bool FSELevelFilter::IsSaveTag(const FName& Tag) +{ + return Tag == TagNoTransform || Tag == TagNoPhysics || Tag == TagNoTags; +} + +bool FSELevelFilter::StoresTransform(const AActor* Actor) +{ + return Actor->IsRootComponentMovable() && !HasTag(Actor, TagNoTransform); +} +bool FSELevelFilter::StoresPhysics(const AActor* Actor) +{ + return !HasTag(Actor, TagNoPhysics); +} +bool FSELevelFilter::StoresTags(const AActor* Actor) +{ + return !HasTag(Actor, TagNoTags); +} +bool FSELevelFilter::IsProcedural(const AActor* Actor) +{ + return Actor->HasAnyFlags(RF_WasLoaded | RF_LoadCompleted); +} + +bool FSELevelFilter::HasTag(const AActor* Actor, const FName Tag) +{ + check(Actor); + return Actor->ActorHasTag(Tag); +} + +bool FSELevelFilter::HasTag(const UActorComponent* Component, const FName Tag) +{ + check(Component); + return Component->ComponentHasTag(Tag); +} diff --git a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp index 1c1bf17..4b55b98 100644 --- a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp +++ b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp @@ -9,7 +9,8 @@ void FSlotHelpers::FindSlotFileNames(TArray& FoundSlots) { FFindSlotVisitor Visitor{FoundSlots}; - FPlatformFileManager::Get().GetPlatformFile().IterateDirectory(*FFileAdapter::GetSaveFolder(), Visitor); + FPlatformFileManager::Get().GetPlatformFile().IterateDirectory( + *FSaveFileHelpers::GetSaveFolder(), Visitor); } bool FSlotHelpers::FFindSlotVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) diff --git a/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp b/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp index 06b5722..41b2ade 100644 --- a/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp @@ -2,16 +2,14 @@ #include "Multithreading/DeleteSlotsTask.h" -#include "FileAdapter.h" #include "HAL/FileManager.h" #include "Misc/SlotHelpers.h" +#include "SaveFileHelpers.h" #include "SaveManager.h" -#include "SavePreset.h" #include - FDeleteSlotsTask::FDeleteSlotsTask(const USaveManager* InManager, FName SlotName) : Manager(InManager) { check(Manager); @@ -26,8 +24,8 @@ void FDeleteSlotsTask::DoWork() if (!SpecificSlotName.IsEmpty()) { // Delete a single slot by id - const FString ScreenshotPath = FFileAdapter::GetThumbnailPath(SpecificSlotName); - bool bIsDeleteSlotSuccess = FFileAdapter::DeleteFile(SpecificSlotName); + const FString ScreenshotPath = FSaveFileHelpers::GetThumbnailPath(SpecificSlotName); + bool bIsDeleteSlotSuccess = FSaveFileHelpers::DeleteFile(SpecificSlotName); bool bIsDeleteScreenshotSuccess = IFileManager::Get().Delete(*ScreenshotPath, true); bSuccess = bIsDeleteSlotSuccess || bIsDeleteScreenshotSuccess; } @@ -38,7 +36,7 @@ void FDeleteSlotsTask::DoWork() for (const FString& File : FoundSlots) { - FFileAdapter::DeleteFile(File); + FSaveFileHelpers::DeleteFile(File); } bSuccess = true; } diff --git a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp index 30fc7fa..28d4d0e 100644 --- a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp @@ -2,8 +2,6 @@ #include "Multithreading/LoadFileTask.h" -#include "SavePreset.h" - ///////////////////////////////////////////////////// // FSaveFileTask diff --git a/Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp similarity index 78% rename from Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp rename to Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp index dd72894..1304670 100644 --- a/Source/SaveExtension/Private/Multithreading/LoadSlotInfosTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp @@ -1,16 +1,15 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Multithreading/LoadSlotInfosTask.h" +#include "Multithreading/LoadSlotsTask.h" -#include "FileAdapter.h" #include "Misc/SlotHelpers.h" +#include "SaveFileHelpers.h" #include "SaveManager.h" -#include "SavePreset.h" #include -void FLoadSlotInfosTask::DoWork() +void FLoadSlotsTask::DoWork() { if (!Manager) { @@ -33,7 +32,7 @@ void FLoadSlotInfosTask::DoWork() for (const FString& FileName : FileNames) { // Load all files - FScopedFileReader Reader(FFileAdapter::GetSlotPath(FileName)); + FScopedFileReader Reader(FSaveFileHelpers::GetSlotPath(FileName)); if (Reader.IsValid()) { auto& File = LoadedFiles.AddDefaulted_GetRef(); @@ -45,7 +44,9 @@ void FLoadSlotInfosTask::DoWork() LoadedSlots.Reserve(LoadedFiles.Num()); for (const auto& File : LoadedFiles) { - LoadedSlots.Add(File.CreateAndDeserializeInfo(Manager)); + USaveSlot* Slot = nullptr; + File.CreateAndDeserializeSlot(Slot, Manager); + LoadedSlots.Add(Slot); } if (!bLoadingSingleInfo && bSortByRecent) @@ -56,7 +57,7 @@ void FLoadSlotInfosTask::DoWork() } } -void FLoadSlotInfosTask::AfterFinish() +void FLoadSlotsTask::AfterFinish() { for (auto& Slot : LoadedSlots) { diff --git a/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp b/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp deleted file mode 100644 index bf243eb..0000000 --- a/Source/SaveExtension/Private/Multithreading/SaveFileTask.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Multithreading/SaveFileTask.h" - -#include "SavePreset.h" - - -///////////////////////////////////////////////////// -// FSaveFileTask diff --git a/Source/SaveExtension/Private/FileAdapter.cpp b/Source/SaveExtension/Private/SaveFileHelpers.cpp similarity index 82% rename from Source/SaveExtension/Private/FileAdapter.cpp rename to Source/SaveExtension/Private/SaveFileHelpers.cpp index d5aa543..7a458cf 100644 --- a/Source/SaveExtension/Private/FileAdapter.cpp +++ b/Source/SaveExtension/Private/SaveFileHelpers.cpp @@ -1,9 +1,8 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "FileAdapter.h" +#include "SaveFileHelpers.h" #include "Multithreading/SaveFileTask.h" -#include "SavePreset.h" #include "SaveSlot.h" #include "SaveSlotData.h" @@ -187,16 +186,16 @@ void FSaveFile::Write(FScopedFileWriter& Writer, bool bCompressData) Ar.Close(); } -void FSaveFile::SerializeInfo(USaveSlot* SlotInfo) +void FSaveFile::SerializeInfo(USaveSlot* Slot) { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::SerializeInfo); - check(SlotInfo); + check(Slot); InfoBytes.Reset(); - InfoClassName = SlotInfo->GetClass()->GetPathName(); + InfoClassName = Slot->GetClass()->GetPathName(); FMemoryWriter BytesWriter(InfoBytes); FObjectAndNameAsStringProxyArchive Ar(BytesWriter, false); - SlotInfo->Serialize(Ar); + Slot->Serialize(Ar); } void FSaveFile::SerializeData(USaveSlotData* SlotData) { @@ -210,25 +209,25 @@ void FSaveFile::SerializeData(USaveSlotData* SlotData) SlotData->Serialize(Ar); } -USaveSlot* FSaveFile::CreateAndDeserializeInfo(const UObject* Outer) const +void FSaveFile::CreateAndDeserializeSlot(USaveSlot*& Slot, const UObject* Outer) const { - TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeInfo); - UObject* Slot = nullptr; - FFileAdapter::DeserializeObject(Slot, InfoClassName, Outer, InfoBytes); - return Cast(Slot); + TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeSlot); + UObject* Object = nullptr; + FSaveFileHelpers::DeserializeObject(Object, InfoClassName, Outer, InfoBytes); + Slot = Cast(Object); } void FSaveFile::CreateAndDeserializeData(USaveSlot* Slot) const { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeData); - UObject* Data = nullptr; - FFileAdapter::DeserializeObject(Data, DataClassName, Slot, DataBytes); - Slot->AssignData(Cast(Data)); + UObject* Object = nullptr; + FSaveFileHelpers::DeserializeObject(Object, DataClassName, Slot, DataBytes); + Slot->AssignData(Cast(Object)); } -bool FFileAdapter::SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression) +bool FSaveFileHelpers::SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression) { - TRACE_CPUPROFILER_EVENT_SCOPE(FFileAdapter::SaveFile); + TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFileHelpers::SaveFile); if (SlotName.IsEmpty()) { @@ -253,52 +252,52 @@ bool FFileAdapter::SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bU return false; } -USaveSlot* FFileAdapter::LoadFile(FStringView SlotName, bool bLoadData, const UObject* Outer) +bool FSaveFileHelpers::LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLoadData, const UObject* Outer) { - TRACE_CPUPROFILER_EVENT_SCOPE(FFileAdapter::LoadFile); + TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFileHelpers::LoadFile); FScopedFileReader Reader(GetSlotPath(SlotName)); if (Reader.IsValid()) { FSaveFile File{}; File.Read(Reader, !bLoadData); - USaveSlot* Slot = File.CreateAndDeserializeInfo(Outer); + File.CreateAndDeserializeSlot(Slot, Outer); if (bLoadData) { File.CreateAndDeserializeData(Slot); } - return Slot; + return true; } - return nullptr; + return false; } -bool FFileAdapter::DeleteFile(FStringView SlotName) +bool FSaveFileHelpers::DeleteFile(FStringView SlotName) { return IFileManager::Get().Delete(*GetSlotPath(SlotName), true, false, true); } -bool FFileAdapter::FileExists(FStringView SlotName) +bool FSaveFileHelpers::FileExists(FStringView SlotName) { return IFileManager::Get().FileSize(*GetSlotPath(SlotName)) >= 0; } -const FString& FFileAdapter::GetSaveFolder() +const FString& FSaveFileHelpers::GetSaveFolder() { static const FString Folder = FString::Printf(TEXT("%sSaveGames/"), *FPaths::ProjectSavedDir()); return Folder; } -FString FFileAdapter::GetSlotPath(FStringView SlotName) +FString FSaveFileHelpers::GetSlotPath(FStringView SlotName) { return GetSaveFolder() / FString::Printf(TEXT("%s.sav"), SlotName.GetData()); } -FString FFileAdapter::GetThumbnailPath(FStringView SlotName) +FString FSaveFileHelpers::GetThumbnailPath(FStringView SlotName) { return GetSaveFolder() / FString::Printf(TEXT("%s.png"), SlotName.GetData()); } -void FFileAdapter::DeserializeObject( +void FSaveFileHelpers::DeserializeObject( UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes) { if (ClassName.IsEmpty() || Bytes.Num() <= 0) @@ -306,7 +305,7 @@ void FFileAdapter::DeserializeObject( return; } - UClass* ObjectClass = FindObject(ANY_PACKAGE, ClassName.GetData()); + UClass* ObjectClass = FindObject(nullptr, ClassName.GetData()); if (!ObjectClass) { ObjectClass = LoadObject(nullptr, ClassName.GetData()); diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index bf6ecd4..e550599 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -2,10 +2,10 @@ #include "SaveManager.h" -#include "FileAdapter.h" #include "LatentActions/LoadInfosAction.h" #include "Multithreading/DeleteSlotsTask.h" -#include "Multithreading/LoadSlotInfosTask.h" +#include "Multithreading/LoadSlotsTask.h" +#include "SaveFileHelpers.h" #include "SaveSettings.h" #include "Serialization/SlotDataTask_LevelLoader.h" #include "Serialization/SlotDataTask_LevelSaver.h" @@ -49,7 +49,7 @@ void USaveManager::Deinitialize() MTTasks.CancelAll(); - if (GetActivePreset()->bSaveOnExit) + if (GetActiveSlot()->bSaveOnClose) SaveCurrentSlot(); FCoreUObjectDelegates::PreLoadMap.RemoveAll(this); @@ -63,15 +63,14 @@ bool USaveManager::SaveSlot(FName SlotName, bool bOverrideIfNeeded, bool bScreen if (!CanLoadOrSave()) return false; - const USavePreset* Preset = GetActivePreset(); if (SlotName.IsNone()) { - SELog(Preset, "Can't use an empty slot name to save.", true); + SELog(ActiveSlot, "Can't use an empty slot name to save.", true); return false; } // Saving - SELog(Preset, "Saving to Slot " + SlotName.ToString()); + SELog(ActiveSlot, "Saving to Slot " + SlotName.ToString()); UWorld* World = GetWorld(); check(World); @@ -116,9 +115,9 @@ bool USaveManager::DeleteSlot(FName SlotName) return bSuccess; } -void USaveManager::FindAllSlots(bool bSortByRecent, FOnSlotInfosLoaded Delegate) +void USaveManager::FindAllSlots(bool bSortByRecent, FOnSlotsLoaded Delegate) { - MTTasks.CreateTask(this, bSortByRecent, MoveTemp(Delegate)) + MTTasks.CreateTask(this, bSortByRecent, MoveTemp(Delegate)) .OnFinished([](auto& Task) { Task->AfterFinish(); }) @@ -127,10 +126,10 @@ void USaveManager::FindAllSlots(bool bSortByRecent, FOnSlotInfosLoaded Delegate) void USaveManager::FindAllSlotsSync(bool bSortByRecent, TArray& Slots) { - auto Delegate = [](const TArray& FoundSlots) { + auto Delegate = FOnSlotsLoaded::CreateLambda([&Slots](const TArray& FoundSlots) { Slots = FoundSlots; - }; - MTTasks.CreateTask(this, bSortByRecent, Delegate) + }); + MTTasks.CreateTask(this, bSortByRecent, Delegate) .OnFinished([](auto& Task) { Task->AfterFinish(); }) @@ -216,7 +215,7 @@ void USaveManager::BPDeleteAllSlots(EDeleteSlotsResult& Result, struct FLatentAc bool USaveManager::IsSlotSaved(FName SlotName) const { - return FFileAdapter::FileExists(SlotName.ToString()); + return FSaveFileHelpers::FileExists(SlotName.ToString()); } bool USaveManager::CanLoadOrSave() @@ -230,15 +229,18 @@ bool USaveManager::CanLoadOrSave() return IsValid(GetWorld()); } -void USaveManager::AssureActiveSlot(bool bForced) +void USaveManager::AssureActiveSlot(TSubclassOf ActiveSlotClass, bool bForced) { if (IsInSlot() && !bForced) return; - UClass* ActiveSlotClass = GetDefault()->ActiveSlot.Get(); if (!ActiveSlotClass) { - ActiveSlotClass = USaveSlot::StaticClass(); + ActiveSlotClass = GetDefault()->ActiveSlot.Get(); + if (!ActiveSlotClass) + { + ActiveSlotClass = USaveSlot::StaticClass(); + } } ActiveSlot = NewObject(this, ActiveSlotClass); } @@ -284,11 +286,11 @@ USaveSlot* USaveManager::LoadInfo(FName SlotName) { if (SlotName.IsNone()) { - SELog(GetActivePreset(), "Invalid Slot. Cant go under 0 or exceed MaxSlots", true); + SELog(ActiveSlot, "Invalid Slot. Cant go under 0 or exceed MaxSlots", true); return nullptr; } - auto& Task = MTTasks.CreateTask(this, SlotName).OnFinished([](auto& Task) { + auto& Task = MTTasks.CreateTask(this, SlotName).OnFinished([](auto& Task) { Task->AfterFinish(); }); Task.StartSynchronousTask(); @@ -302,7 +304,7 @@ USaveSlot* USaveManager::LoadInfo(FName SlotName) USaveSlotDataTask* USaveManager::CreateTask(TSubclassOf TaskType) { USaveSlotDataTask* Task = NewObject(this, TaskType.Get()); - Task->Prepare(CurrentInfo->GetData(), *GetActivePreset()); + Task->Prepare(ActiveSlot); Tasks.Add(Task); return Task; } @@ -318,14 +320,15 @@ void USaveManager::FinishTask(USaveSlotDataTask* Task) } } -FName USaveManager::GetSlotNameFromId(const int32 SlotId) const +FName USaveManager::GetFileNameFromId(const int32 SlotId) const { - if (const auto* Preset = GetActivePreset()) - { - FName Name; - Preset->BPGetSlotNameFromId(SlotId, Name); - return Name; - } + // TODO: Expose custom names + // if (const auto* Preset = GetActivePreset()) + //{ + // FName Name; + // Preset->BPGetSlotNameFromId(SlotId, Name); + // return Name; + //} return FName{FString::FromInt(SlotId)}; } @@ -393,7 +396,7 @@ void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bErro if (!bError) { - OnGameSaved.Broadcast(CurrentInfo); + OnGameSaved.Broadcast(ActiveSlot); } } @@ -430,13 +433,13 @@ void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bErro if (!bError) { - OnGameLoaded.Broadcast(CurrentInfo); + OnGameLoaded.Broadcast(ActiveSlot); } } void USaveManager::OnMapLoadStarted(const FString& MapName) { - SELog(GetActivePreset(), "Loading Map '" + MapName + "'", FColor::Purple); + SELog(ActiveSlot, "Loading Map '" + MapName + "'", FColor::Purple); } void USaveManager::OnMapLoadFinished(UWorld* LoadedWorld) diff --git a/Source/SaveExtension/Private/SavePreset.cpp b/Source/SaveExtension/Private/SavePreset.cpp deleted file mode 100644 index 2fbcd9c..0000000 --- a/Source/SaveExtension/Private/SavePreset.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "SavePreset.h" - -#include "LevelFilter.h" -#include "SaveSlot.h" -#include "SaveSlotData.h" - - -void USavePreset::BPGetSlotNameFromId_Implementation(int32 Id, FName& Name) const -{ - // Call C++ inheritance chain by default - return GetSlotNameFromId(Id, Name); -} - -FSELevelFilter USavePreset::ToFilter() const -{ - FSELevelFilter Filter{}; - Filter.FromPreset(*this); - return Filter; -} diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 33c653d..71bd96a 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -13,17 +13,18 @@ USaveSlot::USaveSlot() { - Data = NewObject(this, DataClass); + Data = NewObject(this, DataClass, TEXT("SlotData")); } -bool USaveSlot::OnSetId(int32 Id) +bool USaveSlot::OnSetIndex(int32 Index) { - FileName = FName{FString::FromInt(SlotId)}; + FileName = FName{FString::FromInt(Index)}; + return true; } -int32 USaveSlot::OnGetId() const +int32 USaveSlot::OnGetIndex() const { - return FCString::Atoi(FileName.ToString()); + return FCString::Atoi(*FileName.ToString()); } UTexture2D* USaveSlot::GetThumbnail() const @@ -73,7 +74,7 @@ bool USaveSlot::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height if (auto* Viewport = GEngine->GameViewport->Viewport) { - _SetThumbnailPath(FFileAdapter::GetThumbnailPath(FileName.ToString())); + _SetThumbnailPath(FSaveFileHelpers::GetThumbnailPath(FileName.ToString())); // TODO: Removal of a thumbnail should be standarized in a function IFileManager& FM = IFileManager::Get(); @@ -96,14 +97,14 @@ bool USaveSlot::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height } -int32 USaveSlot::GetMaxIds() const +int32 USaveSlot::GetMaxIndexes() const { return MaxSlots <= 0 ? 16384 : MaxSlots; } -bool USaveSlot::IsValidId(int32 CheckedId) +bool USaveSlot::IsValidIndex(int32 Index) const { - return CheckedId >= 0 && CheckedId < GetMaxIds(); + return Index >= 0 && Index < GetMaxIndexes(); } void USaveSlot::_SetThumbnailPath(const FString& Path) @@ -115,12 +116,72 @@ void USaveSlot::_SetThumbnailPath(const FString& Path) } } -bool USaveSlot::SetId_Implementation(int32 Id) +bool USaveSlot::SetIndex_Implementation(int32 Index) { - return OnSetId(Id); + return OnSetIndex(Index); } -int32 USaveSlot::GetId_Implementation() const +int32 USaveSlot::GetIndex_Implementation() const { - return OnGetId(); -} \ No newline at end of file + return OnGetIndex(); +} + + +const FSEActorClassFilter& USaveSlot::GetActorFilter(bool bIsLoading) const +{ + return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; +} + +const FSEComponentClassFilter& USaveSlot::GetComponentFilter(bool bIsLoading) const +{ + return (bIsLoading && bUseLoadActorFilter) ? LoadComponentFilter : ComponentFilter; +} + +bool USaveSlot::IsMTSerializationLoad() const +{ + return MultithreadedSerialization == ESaveASyncMode::LoadAsync || + MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; +} +bool USaveSlot::IsMTSerializationSave() const +{ + return MultithreadedSerialization == ESaveASyncMode::SaveAsync || + MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; +} + +ESaveASyncMode USaveSlot::GetFrameSplitSerialization() const +{ + return FrameSplittedSerialization; +} +float USaveSlot::GetMaxFrameMs() const +{ + return MaxFrameMs; +} + +bool USaveSlot::IsFrameSplitLoad() const +{ + return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESaveASyncMode::LoadAsync || + FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); +} +bool USaveSlot::IsFrameSplitSave() const +{ + return !IsMTSerializationSave() && (FrameSplittedSerialization == ESaveASyncMode::SaveAsync || + FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); +} + +bool USaveSlot::IsMTFilesLoad() const +{ + return MultithreadedFiles == ESaveASyncMode::LoadAsync || + MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; +} +bool USaveSlot::IsMTFilesSave() const +{ + return MultithreadedFiles == ESaveASyncMode::SaveAsync || + MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; +} + +FSELevelFilter USaveSlot::ToFilter() const +{ + FSELevelFilter Filter{}; + Filter.FromSlot(*this); + return Filter; +} diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index f94ed8c..c3b7786 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -2,8 +2,6 @@ #include "SaveSlotData.h" -#include "SavePreset.h" - #include @@ -18,7 +16,7 @@ void USaveSlotData::Serialize(FArchive& Ar) Ar << GameInstance; static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; - LevelFilterType->SerializeItem(Ar, &GeneralLevelFilter, nullptr); + LevelFilterType->SerializeItem(Ar, &GlobalLevelFilter, nullptr); MainLevel.Serialize(Ar); Ar << SubLevels; } diff --git a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp index 9ae45ee..da85dfa 100644 --- a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp +++ b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp @@ -15,8 +15,8 @@ bool FLevelRecord::Serialize(FArchive& Ar) { Super::Serialize(Ar); - Ar << bOverrideGeneralFilter; - if (bOverrideGeneralFilter) + Ar << bOverrideGlobalFilter; + if (bOverrideGlobalFilter) { static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; LevelFilterType->SerializeItem(Ar, &Filter, nullptr); diff --git a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp index e84cc0e..16e4bac 100644 --- a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp +++ b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp @@ -3,7 +3,6 @@ #include "Serialization/MTTask_SerializeActors.h" #include "SaveManager.h" -#include "SavePreset.h" #include "SaveSlot.h" #include "SaveSlotData.h" #include "Serialization/SEArchive.h" @@ -12,7 +11,6 @@ #include - ///////////////////////////////////////////////////// // FMTTask_SerializeActors void FMTTask_SerializeActors::DoWork() diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp index 1f84b0b..b25eb4c 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp @@ -3,7 +3,6 @@ #include "Serialization/SlotDataTask.h" #include "SaveManager.h" -#include "SavePreset.h" ///////////////////////////////////////////////////// @@ -47,35 +46,35 @@ USaveManager* USaveSlotDataTask::GetManager() const void USaveSlotDataTask::BakeAllFilters() { TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask::BakeAllFilters); - SlotData->GeneralLevelFilter.BakeAllowedClasses(); + SlotData->GlobalLevelFilter.BakeAllowedClasses(); - if (SlotData->MainLevel.bOverrideGeneralFilter) + if (SlotData->MainLevel.bOverrideGlobalFilter) { SlotData->MainLevel.Filter.BakeAllowedClasses(); } for (const auto& Level : SlotData->SubLevels) { - if (Level.bOverrideGeneralFilter) + if (Level.bOverrideGlobalFilter) { Level.Filter.BakeAllowedClasses(); } } } -const FSELevelFilter& USaveSlotDataTask::GetGeneralFilter() const +const FSELevelFilter& USaveSlotDataTask::GetGlobalFilter() const { check(SlotData); - return SlotData->GeneralLevelFilter; + return SlotData->GlobalLevelFilter; } const FSELevelFilter& USaveSlotDataTask::GetLevelFilter(const FLevelRecord& Level) const { - if (Level.bOverrideGeneralFilter) + if (Level.bOverrideGlobalFilter) { return Level.Filter; } - return GetGeneralFilter(); + return GetGlobalFilter(); } FLevelRecord* USaveSlotDataTask::FindLevelRecord(const ULevelStreaming* Level) const diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp index d9c87fc..9e460ab 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp @@ -19,7 +19,7 @@ void USaveSlotDataTask_LevelLoader::OnStart() GetLevelFilter(*LevelRecord).BakeAllowedClasses(); - if (Preset->IsFrameSplitLoad()) + if (Slot->IsFrameSplitLoad()) { DeserializeLevelASync(StreamingLevel->GetLoadedLevel(), StreamingLevel); } diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp index 814d05a..6cde5f2 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp @@ -4,7 +4,6 @@ #include "Misc/SlotHelpers.h" #include "SaveManager.h" -#include "SavePreset.h" #include "Serialization/SEArchive.h" #include @@ -48,12 +47,11 @@ void USaveSlotDataTask_Loader::OnStart() TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnStart); USaveManager* Manager = GetManager(); - SELog(Preset, "Loading from Slot " + SlotName.ToString()); - - NewSlotInfo = Manager->LoadInfo(SlotName); - if (!NewSlotInfo) + Slot = Manager->LoadInfo(SlotName); + SELog(Slot, "Loading from Slot " + SlotName.ToString()); + if (!Slot) { - SELog(Preset, "Slot Info not found! Can't load.", FColor::White, true, 1); + SELog(Slot, "Slot Info not found! Can't load.", FColor::White, true, 1); Finish(false); return; } @@ -66,23 +64,23 @@ void USaveSlotDataTask_Loader::OnStart() // Cross-Level loading // TODO: Handle empty Map as empty world FName CurrentMapName{FSlotHelpers::GetWorldName(World)}; - if (CurrentMapName != NewSlotInfo->Map) + if (CurrentMapName != Slot->Map) { LoadState = ELoadDataTaskState::LoadingMap; - FString MapToOpen = NewSlotInfo->Map.ToString(); + FString MapToOpen = Slot->Map.ToString(); if (!GEngine->MakeSureMapNameIsValid(MapToOpen)) { UE_LOG(LogSaveExtension, Warning, TEXT("Slot '%s' was saved in map '%s' but it did not exist while loading. Corrupted save " "file?"), - *NewSlotInfo->FileName.ToString(), *MapToOpen); + *Slot->FileName.ToString(), *MapToOpen); Finish(false); return; } UGameplayStatics::OpenLevel(this, FName{MapToOpen}); - SELog(Preset, + SELog(Slot, "Slot '" + SlotName.ToString() + "' is recorded on another Map. Loading before charging slot.", FColor::White, false, 1); return; @@ -122,13 +120,13 @@ void USaveSlotDataTask_Loader::OnFinish(bool bSuccess) TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnFinish); if (bSuccess) { - SELog(Preset, "Finished Loading", FColor::Green); + SELog(Slot, "Finished Loading", FColor::Green); } // Execute delegates - Delegate.ExecuteIfBound((bSuccess) ? NewSlotInfo : nullptr); + Delegate.ExecuteIfBound((bSuccess) ? Slot : nullptr); - GetManager()->OnLoadFinished(SlotData ? GetGeneralFilter() : FSELevelFilter{}, !bSuccess); + GetManager()->OnLoadFinished(SlotData ? GetGlobalFilter() : FSELevelFilter{}, !bSuccess); } void USaveSlotDataTask_Loader::BeginDestroy() @@ -156,7 +154,7 @@ void USaveSlotDataTask_Loader::OnMapLoaded() Finish(false); } const FName NewMapName{FSlotHelpers::GetWorldName(World)}; - if (NewMapName == NewSlotInfo->Map) + if (NewMapName == Slot->Map) { if (IsDataLoaded()) { @@ -172,7 +170,7 @@ void USaveSlotDataTask_Loader::OnMapLoaded() void USaveSlotDataTask_Loader::StartDeserialization() { TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::StartDeserialization); - check(NewSlotInfo); + check(Slot); LoadState = ELoadDataTaskState::Deserializing; @@ -184,17 +182,17 @@ void USaveSlotDataTask_Loader::StartDeserialization() return; } - NewSlotInfo->LoadDate = FDateTime::Now(); + Slot->Stats.LoadDate = FDateTime::Now(); - GetManager()->OnLoadBegan(GetGeneralFilter()); + GetManager()->OnLoadBegan(GetGlobalFilter()); // Apply current Info if succeeded - GetManager()->__SetCurrentInfo(NewSlotInfo); + GetManager()->AssignActiveSlot(Slot); BakeAllFilters(); BeforeDeserialize(); - if (Preset->IsFrameSplitLoad()) + if (Slot->IsFrameSplitLoad()) DeserializeASync(); else DeserializeSync(); @@ -204,7 +202,7 @@ void USaveSlotDataTask_Loader::StartLoadingData() { LoadDataTask = new FAsyncTask(GetManager(), SlotName.ToString()); - if (Preset->IsMTFilesLoad()) + if (Slot->IsMTFilesLoad()) LoadDataTask->StartBackgroundTask(); else LoadDataTask->StartSynchronousTask(); @@ -240,7 +238,7 @@ void USaveSlotDataTask_Loader::DeserializeSync() const UWorld* World = GetWorld(); check(World); - SELog(Preset, "World '" + World->GetName() + "'", FColor::Green, false, 1); + SELog(Slot, "World '" + World->GetName() + "'", FColor::Green, false, 1); PrepareAllLevels(); @@ -271,7 +269,7 @@ void USaveSlotDataTask_Loader::DeserializeLevelSync( const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; - SELog(Preset, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); + SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); if (FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel)) { @@ -292,7 +290,7 @@ void USaveSlotDataTask_Loader::DeserializeASync() { // Deserialize world { - SELog(Preset, "World '" + GetWorld()->GetName() + "'", FColor::Green, false, 1); + SELog(Slot, "World '" + GetWorld()->GetName() + "'", FColor::Green, false, 1); PrepareAllLevels(); DeserializeLevelASync(GetWorld()->GetCurrentLevel()); @@ -305,7 +303,7 @@ void USaveSlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStream const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; - SELog(Preset, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); + SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); if (!LevelRecord) @@ -428,8 +426,7 @@ void USaveSlotDataTask_Loader::FinishedDeserializing() { // Clean serialization data SlotData->CleanRecords(true); - GetManager()->__SetCurrentData(SlotData); - + Slot->AssignData(SlotData); Finish(true); } @@ -509,7 +506,7 @@ void USaveSlotDataTask_Loader::DeserializeGameInstance() GameInstance->Serialize(Archive); } - SELog(Preset, "Game Instance '" + Record.Name.ToString() + "'", FColor::Green, !bSuccess, 1); + SELog(Slot, "Game Instance '" + Record.Name.ToString() + "'", FColor::Green, !bSuccess, 1); } bool USaveSlotDataTask_Loader::DeserializeActor( @@ -573,7 +570,7 @@ void USaveSlotDataTask_Loader::DeserializeActorComponents( const FComponentRecord* Record = ActorRecord.ComponentRecords.FindByKey(Component); if (!Record) { - SELog(Preset, "Component '" + Component->GetFName().ToString() + "' - Record not found", + SELog(Slot, "Component '" + Component->GetFName().ToString() + "' - Record not found", FColor::Red, false, Indent + 1); continue; } diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp index 6a81d3b..971d1ab 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp +++ b/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp @@ -2,10 +2,9 @@ #include "Serialization/SlotDataTask_Saver.h" -#include "FileAdapter.h" #include "Misc/SlotHelpers.h" +#include "SaveFileHelpers.h" #include "SaveManager.h" -#include "SavePreset.h" #include "SaveSlot.h" #include "SaveSlotData.h" @@ -20,19 +19,19 @@ void USaveSlotDataTask_Saver::OnStart() { TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnStart); USaveManager* Manager = GetManager(); - Manager->TryInstantiateInfo(); + Manager->AssureActiveSlot(); bool bSave = true; const FString SlotNameStr = SlotName.ToString(); // Overriding { - const bool bFileExists = FFileAdapter::FileExists(SlotNameStr); + const bool bFileExists = FSaveFileHelpers::FileExists(SlotNameStr); if (bOverride) { // Delete previous save if (bFileExists) { - FFileAdapter::DeleteFile(SlotNameStr); + FSaveFileHelpers::DeleteFile(SlotNameStr); } } else @@ -47,26 +46,26 @@ void USaveSlotDataTask_Saver::OnStart() { const UWorld* World = GetWorld(); - GetManager()->OnSaveBegan(GetGeneralFilter()); + GetManager()->OnSaveBegan(GetGlobalFilter()); - SlotInfo = Manager->GetCurrentInfo(); - SlotData = Manager->GetCurrentData(); + Slot = Manager->GetActiveSlot(); + SlotData = Slot->GetData(); SlotData->CleanRecords(true); - check(SlotInfo && SlotData); + check(Slot && SlotData); - const bool bSlotExisted = SlotInfo->FileName == SlotName; - SlotInfo->FileName = SlotName; + const bool bSlotExisted = Slot->FileName == SlotName; + Slot->FileName = SlotName; if (bSaveThumbnail) { - SlotInfo->CaptureThumbnail(Width, Height); + Slot->CaptureThumbnail(Width, Height); } // Time stats { - SlotInfo->SaveDate = FDateTime::Now(); - FSaveSlotStats& Stats = SlotInfo->Stats; + FSaveSlotStats& Stats = Slot->Stats; + Stats.SaveDate = FDateTime::Now(); // If this info has been loaded ever const bool bWasLoaded = Stats.LoadDate.GetTicks() > 0; @@ -89,11 +88,11 @@ void USaveSlotDataTask_Saver::OnStart() } // Save Level info in both files - SlotInfo->Map = FName{FSlotHelpers::GetWorldName(World)}; + Slot->Map = FName{FSlotHelpers::GetWorldName(World)}; SlotData->Map = SlotData->Map; - SlotData->bStoreGameInstance = Preset->bStoreGameInstance; - SlotData->GeneralLevelFilter = Preset->ToFilter(); + SlotData->bStoreGameInstance = Slot->bStoreGameInstance; + SlotData->GlobalLevelFilter = Slot->ToFilter(); SerializeWorld(); SaveFile(); @@ -111,7 +110,7 @@ void USaveSlotDataTask_Saver::Tick(float DeltaTime) { if (bSaveThumbnail) { - if (SlotInfo && SlotInfo->GetThumbnail()) + if (Slot && Slot->GetThumbnail()) { Finish(true); } @@ -131,14 +130,14 @@ void USaveSlotDataTask_Saver::OnFinish(bool bSuccess) // Clean serialization data SlotData->CleanRecords(true); - SELog(Preset, "Finished Saving", FColor::Green); + SELog(Slot, "Finished Saving", FColor::Green); } // Execute delegates USaveManager* Manager = GetManager(); check(Manager); - Delegate.ExecuteIfBound((Manager && bSuccess) ? Manager->GetCurrentInfo() : nullptr); - Manager->OnSaveFinished(SlotData ? GetGeneralFilter() : FSELevelFilter{}, !bSuccess); + Delegate.ExecuteIfBound((Manager && bSuccess) ? Manager->GetActiveSlot() : nullptr); + Manager->OnSaveFinished(SlotData ? GetGlobalFilter() : FSELevelFilter{}, !bSuccess); } void USaveSlotDataTask_Saver::BeginDestroy() @@ -163,7 +162,7 @@ void USaveSlotDataTask_Saver::SerializeWorld() } const UWorld* World = GetWorld(); - SELog(Preset, "World '" + World->GetName() + "'", FColor::Green, false, 1); + SELog(Slot, "World '" + World->GetName() + "'", FColor::Green, false, 1); const TArray& Levels = World->GetStreamingLevels(); PrepareAllLevels(Levels); @@ -205,14 +204,14 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeLevelSync); check(IsValid(Level)); - if (!Preset->IsMTSerializationSave()) + if (!Slot->IsMTSerializationSave()) { AssignedTasks = 1; } const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; - SELog(Preset, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); + SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level FLevelRecord* LevelRecord = &SlotData->MainLevel; @@ -257,7 +256,7 @@ void USaveSlotDataTask_Saver::RunScheduledTasks() { for (int32 I = 1; I < Tasks.Num(); ++I) { - if (Preset->IsMTSerializationSave()) + if (Slot->IsMTSerializationSave()) Tasks[I].StartBackgroundTask(); else Tasks[I].StartSynchronousTask(); @@ -284,9 +283,9 @@ void USaveSlotDataTask_Saver::SaveFile() USaveManager* Manager = GetManager(); SaveTask = - new FAsyncTask(Manager->GetActiveSlot(), SlotName.ToString(), Preset->bUseCompression); + new FAsyncTask(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); - if (Preset->IsMTFilesSave()) + if (Slot->IsMTFilesSave()) { SaveTask->StartBackgroundTask(); } diff --git a/Source/SaveExtension/Public/ISaveExtension.h b/Source/SaveExtension/Public/ISaveExtension.h index 5230ae8..f8b644c 100644 --- a/Source/SaveExtension/Public/ISaveExtension.h +++ b/Source/SaveExtension/Public/ISaveExtension.h @@ -4,8 +4,7 @@ #include "Engine/Engine.h" #include "Modules/ModuleManager.h" -#include "SavePreset.h" - +#include "SaveSlot.h" DECLARE_LOG_CATEGORY_EXTERN(LogSaveExtension, All, All); @@ -22,15 +21,15 @@ class ISaveExtension : public IModuleInterface return FModuleManager::Get().IsModuleLoaded("SaveExtension"); } - static void Log(const USavePreset* Preset, const FString Message, bool bError) + static void Log(const USaveSlot* Slot, const FString& Message, bool bError) { - Log(Preset, Message, FColor::White, bError, 2.f); + Log(Slot, Message, FColor::White, bError, 2.f); } - static void Log(const USavePreset* Preset, const FString Message, FColor Color = FColor::White, + static void Log(const USaveSlot* Slot, const FString& Message, FColor Color = FColor::White, bool bError = false, const float Duration = 2.f) { - if (Preset->bDebug) + if (Slot->bDebug) { if (bError) { @@ -48,7 +47,7 @@ class ISaveExtension : public IModuleInterface UE_LOG(LogSaveExtension, Log, TEXT("%s"), *ComposedMessage); } - if (Preset->bDebugInScreen && GEngine) + if (Slot->bDebugInScreen && GEngine) { GEngine->AddOnScreenDebugMessage(-1, Duration, Color, ComposedMessage); } diff --git a/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h b/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h index 1e624b7..e855625 100644 --- a/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h +++ b/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h @@ -2,7 +2,7 @@ #pragma once -#include "Multithreading/LoadSlotInfosTask.h" +#include "Multithreading/LoadSlotsTask.h" #include #include @@ -27,7 +27,7 @@ class FLoadInfosAction : public FPendingLatentAction public: ELoadInfoResult& Result; - TArray& SlotInfos; + TArray& Slots; bool bFinished; FName ExecutionFunction; diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 2b55c68..5a3641f 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -2,12 +2,13 @@ #pragma once -#include "SavePreset.h" +#include "Misc/ClassFilter.h" #include "LevelFilter.generated.h" class USaveManager; +class USaveSlot; /** @@ -41,87 +42,27 @@ struct FSELevelFilter FSEComponentClassFilter LoadComponentFilter; - FSELevelFilter() {} - - void FromPreset(const USavePreset& Preset) - { - ActorFilter = Preset.GetActorFilter(true); - LoadActorFilter = Preset.GetActorFilter(false); - bStoreComponents = Preset.bStoreComponents; - ComponentFilter = Preset.GetComponentFilter(true); - LoadComponentFilter = Preset.GetComponentFilter(false); - } - - void BakeAllowedClasses() const - { - TRACE_CPUPROFILER_EVENT_SCOPE(FSELevelFilter::BakeAllowedClasses); - ActorFilter.BakeAllowedClasses(); - ComponentFilter.BakeAllowedClasses(); - LoadActorFilter.BakeAllowedClasses(); - LoadComponentFilter.BakeAllowedClasses(); - } - - bool ShouldSave(const AActor* Actor) const - { - return ActorFilter.IsClassAllowed(Actor->GetClass()); - } - - bool ShouldLoad(const AActor* Actor) const - { - return LoadActorFilter.IsClassAllowed(Actor->GetClass()); - } - - bool ShouldSave(const UActorComponent* Component) const - { - return IsValid(Component) && ComponentFilter.IsClassAllowed(Component->GetClass()); - } - - bool ShouldLoad(const UActorComponent* Component) const - { - return IsValid(Component) && LoadComponentFilter.IsClassAllowed(Component->GetClass()); - } - - static bool StoresTransform(const UActorComponent* Component) - { - return Component->GetClass()->IsChildOf() && HasTag(Component, TagTransform); - } - - static bool StoresTags(const UActorComponent* Component) - { - return !HasTag(Component, TagNoTags); - } - - static bool IsSaveTag(const FName& Tag) - { - return Tag == TagNoTransform || Tag == TagNoPhysics || Tag == TagNoTags; - } - - static FORCEINLINE bool StoresTransform(const AActor* Actor) - { - return Actor->IsRootComponentMovable() && !HasTag(Actor, TagNoTransform); - } - static FORCEINLINE bool StoresPhysics(const AActor* Actor) - { - return !HasTag(Actor, TagNoPhysics); - } - static FORCEINLINE bool StoresTags(const AActor* Actor) - { - return !HasTag(Actor, TagNoTags); - } - static FORCEINLINE bool IsProcedural(const AActor* Actor) - { - return Actor->HasAnyFlags(RF_WasLoaded | RF_LoadCompleted); - } - - static FORCEINLINE bool HasTag(const AActor* Actor, const FName Tag) - { - check(Actor); - return Actor->ActorHasTag(Tag); - } - - static FORCEINLINE bool HasTag(const UActorComponent* Component, const FName Tag) - { - check(Component); - return Component->ComponentHasTag(Tag); - } + FSELevelFilter() = default; + + void FromSlot(const USaveSlot& Slot); + + void BakeAllowedClasses() const; + + bool ShouldSave(const AActor* Actor) const; + bool ShouldSave(const UActorComponent* Component) const; + bool ShouldLoad(const AActor* Actor) const; + bool ShouldLoad(const UActorComponent* Component) const; + + static bool StoresTransform(const UActorComponent* Component); + static bool StoresTags(const UActorComponent* Component); + + static bool IsSaveTag(const FName& Tag); + + static bool StoresTransform(const AActor* Actor); + static bool StoresPhysics(const AActor* Actor); + static bool StoresTags(const AActor* Actor); + static bool IsProcedural(const AActor* Actor); + + static bool HasTag(const AActor* Actor, const FName Tag); + static bool HasTag(const UActorComponent* Component, const FName Tag); }; diff --git a/Source/SaveExtension/Public/Misc/SlotHelpers.h b/Source/SaveExtension/Public/Misc/SlotHelpers.h index ae660c1..8acee7b 100644 --- a/Source/SaveExtension/Public/Misc/SlotHelpers.h +++ b/Source/SaveExtension/Public/Misc/SlotHelpers.h @@ -2,13 +2,12 @@ #pragma once -#include "FileAdapter.h" +#include "SaveFileHelpers.h" #include #include - struct FSlotHelpers { static void FindSlotFileNames(TArray& FoundSlots); diff --git a/Source/SaveExtension/Public/Multithreading/Delegates.h b/Source/SaveExtension/Public/Multithreading/Delegates.h index c373048..7870bca 100644 --- a/Source/SaveExtension/Public/Multithreading/Delegates.h +++ b/Source/SaveExtension/Public/Multithreading/Delegates.h @@ -5,7 +5,7 @@ #include -DECLARE_DELEGATE_OneParam(FOnSlotInfosLoaded, const TArray&); +DECLARE_DELEGATE_OneParam(FOnSlotsLoaded, const TArray&); // @param Amount of slots removed DECLARE_DELEGATE(FOnSlotsDeleted); diff --git a/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h b/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h index b36b548..b3ee532 100644 --- a/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h +++ b/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h @@ -2,14 +2,15 @@ #pragma once -#include "FileAdapter.h" #include "Multithreading/Delegates.h" +#include "SaveFileHelpers.h" #include "SaveSlot.h" #include #include + class USaveManager; /** diff --git a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h index d8e850c..5d0d6e7 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h @@ -2,7 +2,7 @@ #pragma once -#include "FileAdapter.h" +#include "SaveFileHelpers.h" #include @@ -16,7 +16,7 @@ class FLoadFileTask : public FNonAbandonableTask TWeakObjectPtr Manager; const FString SlotName; - TWeakObjectPtr SlotInfo; + TWeakObjectPtr Slot; TWeakObjectPtr SlotData; @@ -25,9 +25,9 @@ class FLoadFileTask : public FNonAbandonableTask {} ~FLoadFileTask() { - if (SlotInfo.IsValid()) + if (Slot.IsValid()) { - SlotInfo->ClearInternalFlags(EInternalObjectFlags::Async); + Slot->ClearInternalFlags(EInternalObjectFlags::Async); } if (SlotData.IsValid()) { @@ -37,19 +37,21 @@ class FLoadFileTask : public FNonAbandonableTask void DoWork() { - FScopedFileReader FileReader(FFileAdapter::GetSlotPath(SlotName)); + FScopedFileReader FileReader(FSaveFileHelpers::GetSlotPath(SlotName)); if (FileReader.IsValid()) { FSaveFile File; File.Read(FileReader, false); - SlotInfo = File.CreateAndDeserializeInfo(Manager.Get()); - SlotData = File.CreateAndDeserializeData(Manager.Get()); + USaveSlot* NewSlot = Slot.Get(); + File.CreateAndDeserializeSlot(NewSlot, Manager.Get()); + File.CreateAndDeserializeData(NewSlot); + Slot = NewSlot; } } USaveSlot* GetInfo() { - return SlotInfo.Get(); + return Slot.Get(); } USaveSlotData* GetData() @@ -57,7 +59,7 @@ class FLoadFileTask : public FNonAbandonableTask return SlotData.Get(); } - FORCEINLINE TStatId GetStatId() const + TStatId GetStatId() const { RETURN_QUICK_DECLARE_CYCLE_STAT(FLoadFileTask, STATGROUP_ThreadPoolAsyncTasks); } diff --git a/Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h b/Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h similarity index 63% rename from Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h rename to Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h index 773ca9a..4a709c9 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadSlotInfosTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h @@ -2,8 +2,8 @@ #pragma once -#include "FileAdapter.h" #include "Multithreading/Delegates.h" +#include "SaveFileHelpers.h" #include "SaveSlot.h" #include @@ -13,10 +13,10 @@ class USaveManager; /** - * FLoadSlotInfosTask + * FLoadSlotsTask * Async task to load one or many slot infos */ -class FLoadSlotInfosTask : public FNonAbandonableTask +class FLoadSlotsTask : public FNonAbandonableTask { protected: const USaveManager* Manager; @@ -27,21 +27,19 @@ class FLoadSlotInfosTask : public FNonAbandonableTask TArray LoadedSlots; - FOnSlotInfosLoaded Delegate; + FOnSlotsLoaded Delegate; public: /** All infos Constructor */ - explicit FLoadSlotInfosTask( - const USaveManager* Manager, bool bInSortByRecent, const FOnSlotInfosLoaded& Delegate) + explicit FLoadSlotsTask(const USaveManager* Manager, bool bInSortByRecent, const FOnSlotsLoaded& Delegate) : Manager(Manager) , bSortByRecent(bInSortByRecent) , Delegate(Delegate) {} /** One info Constructor */ - explicit FLoadSlotInfosTask(USaveManager* Manager, FName SlotName) : Manager(Manager), SlotName(SlotName) - {} + explicit FLoadSlotsTask(USaveManager* Manager, FName SlotName) : Manager(Manager), SlotName(SlotName) {} void DoWork(); @@ -55,6 +53,6 @@ class FLoadSlotInfosTask : public FNonAbandonableTask FORCEINLINE TStatId GetStatId() const { - RETURN_QUICK_DECLARE_CYCLE_STAT(FLoadAllSlotInfosTask, STATGROUP_ThreadPoolAsyncTasks); + RETURN_QUICK_DECLARE_CYCLE_STAT(FLoadAllSlotsTask, STATGROUP_ThreadPoolAsyncTasks); } }; diff --git a/Source/SaveExtension/Public/Multithreading/SaveFileTask.h b/Source/SaveExtension/Public/Multithreading/SaveFileTask.h index a3be359..8887cae 100644 --- a/Source/SaveExtension/Public/Multithreading/SaveFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/SaveFileTask.h @@ -2,7 +2,7 @@ #pragma once -#include "FileAdapter.h" +#include "SaveFileHelpers.h" #include @@ -26,7 +26,7 @@ class FSaveFileTask : public FNonAbandonableTask void DoWork() { - FFileAdapter::SaveFile(SlotName, Info, bUseCompression); + FSaveFileHelpers::SaveFile(SlotName, Info, bUseCompression); } FORCEINLINE TStatId GetStatId() const diff --git a/Source/SaveExtension/Public/FileAdapter.h b/Source/SaveExtension/Public/SaveFileHelpers.h similarity index 88% rename from Source/SaveExtension/Public/FileAdapter.h rename to Source/SaveExtension/Public/SaveFileHelpers.h index a0c9a2d..7daa676 100644 --- a/Source/SaveExtension/Public/FileAdapter.h +++ b/Source/SaveExtension/Public/SaveFileHelpers.h @@ -14,7 +14,6 @@ #include -class USavePreset; class USaveSlot; class USaveSlotData; class FMemoryReader; @@ -98,24 +97,24 @@ struct FSaveFile void Read(FScopedFileReader& Reader, bool bSkipData); void Write(FScopedFileWriter& Writer, bool bCompressData); - void SerializeInfo(USaveSlot* SlotInfo); + void SerializeInfo(USaveSlot* Slot); void SerializeData(USaveSlotData* SlotData); - USaveSlot* CreateAndDeserializeInfo(const UObject* Outer) const; + void CreateAndDeserializeSlot(USaveSlot*& Slot, const UObject* Outer) const; void CreateAndDeserializeData(USaveSlot* Slot) const; }; /** Based on GameplayStatics to add multi-threading */ -class SAVEEXTENSION_API FFileAdapter +class SAVEEXTENSION_API FSaveFileHelpers { public: static bool SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression); // Not safe for Multi-threading - static USaveSlot* LoadFile(FStringView SlotName, bool bLoadData, const UObject* Outer); + static bool LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLoadData, const UObject* Outer); static bool DeleteFile(FStringView SlotName); - static bool DoesFileExist(FStringView SlotName); + static bool FileExists(FStringView SlotName); static const FString& GetSaveFolder(); static FString GetSlotPath(FStringView SlotName); diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 4d1c1c2..abd7337 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -10,7 +10,6 @@ #include "Multithreading/Delegates.h" #include "Multithreading/ScopedTaskManager.h" #include "SaveExtensionInterface.h" -#include "SavePreset.h" #include "SaveSlot.h" #include "SaveSlotData.h" #include "Serialization/SlotDataTask.h" @@ -26,8 +25,8 @@ #include "SaveManager.generated.h" -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, SlotInfo); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, SlotInfo); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, Slot); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, Slot); struct FLatentActionInfo; @@ -68,9 +67,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi bool bTickWithGameWorld = false; private: - UPROPERTY(Transient) - USavePreset* ActivePreset; - // Active SaveSlot used for current saves (load on start, periodic save, etc) UPROPERTY() TObjectPtr ActiveSlot; @@ -114,8 +110,8 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi bool SaveSlot(FName SlotName, bool bOverrideIfNeeded = true, bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); - /** Save the Game info an SlotInfo */ - bool SaveSlot(const USaveSlot* SlotInfo, bool bOverrideIfNeeded = true, bool bScreenshot = false, + /** Save the Game info an Slot */ + bool SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded = true, bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Save the Game into an specified slot id */ @@ -133,8 +129,8 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Load game from a slot Id */ bool LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded = {}); - /** Load game from a SlotInfo */ - bool LoadSlot(const USaveSlot* SlotInfo, FOnGameLoaded OnLoaded = {}); + /** Load game from a Slot */ + bool LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded = {}); /** Reload the currently loaded slot if any */ bool ReloadCurrentSlot(FOnGameLoaded OnLoaded = {}) @@ -143,11 +139,11 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi } /** - * Find all saved games and return their SlotInfos + * Find all saved games and return their Slots * @param bSortByRecent Should slots be ordered by save date? * @param SaveInfos All saved games found on disk */ - void FindAllSlots(bool bSortByRecent, FOnSlotInfosLoaded Delegate); + void FindAllSlots(bool bSortByRecent, FOnSlotsLoaded Delegate); void FindAllSlotsSync(bool bSortByRecent, TArray& Slots); /** Delete a saved game on an specified slot name @@ -168,29 +164,33 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlot(FName SlotName, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, + void BPSaveSlot(FName SlotName, bool bScreenshot, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game into an specified Slot */ UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, + void BPSaveSlotById(int32 SlotId, bool bScreenshot, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game to a Slot */ UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Info", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotByInfo(const USaveSlot* SlotInfo, bool bScreenshot, const FScreenshotSize Size, - ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); + void BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreenshot, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, + FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the currently loaded Slot */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Saving", meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Current Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveCurrentSlot( - bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo) + void BPSaveCurrentSlot(bool bScreenshot, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, + FLatentActionInfo LatentInfo) { BPSaveSlotByInfo(ActiveSlot, bScreenshot, Size, Result, MoveTemp(LatentInfo), true); } @@ -207,11 +207,11 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPLoadSlotById(int32 SlotId, ELoadGameResult& Result, FLatentActionInfo LatentInfo); - /** Load game from a SlotInfo */ + /** Load game from a Slot */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", meta = (DisplayName = "Load Slot by Info", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotByInfo(const USaveSlot* SlotInfo, ELoadGameResult& Result, FLatentActionInfo LatentInfo); + void BPLoadSlotByInfo(const USaveSlot* Slot, ELoadGameResult& Result, FLatentActionInfo LatentInfo); /** Reload the currently loaded slot if any */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", @@ -223,7 +223,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi } /** - * Find all saved games and return their SlotInfos + * Find all saved games and return their Slots * @param bSortByRecent Should slots be ordered by save date? * @param SaveInfos All saved games found on disk */ @@ -239,10 +239,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintCallable, Category = "SaveExtension") FORCEINLINE bool DeleteSlotById(int32 SlotId) { - if (!IsValidSlot(SlotId)) - { - return false; - } return DeleteSlot(GetFileNameFromId(SlotId)); } @@ -264,7 +260,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi return Slot ? DeleteSlot(Slot->FileName) : false; } - /** Get the currently loaded SlotInfo. If game was never loaded returns a new SlotInfo */ + /** Get the currently loaded Slot. If game was never loaded returns a new Slot */ UFUNCTION(BlueprintPure, Category = "SaveExtension") FORCEINLINE USaveSlot* GetActiveSlot() { @@ -273,19 +269,19 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi } /** - * Load and return an SlotInfo by Id if it exists + * Load and return an Slot by Id if it exists * Performance: Interacts with disk, could be slow if called frequently - * @param SlotId Id of the SlotInfo to be loaded - * @return the SlotInfo associated with an Id + * @param SlotId Id of the Slot to be loaded + * @return the Slot associated with an Id */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") - FORCEINLINE USaveSlot* GetSlotInfoById(int32 SlotId) + FORCEINLINE USaveSlot* GetSlotById(int32 SlotId) { return LoadInfo(SlotId); } UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") - FORCEINLINE USaveSlot* GetSlotInfo(FName SlotName) + FORCEINLINE USaveSlot* GetSlot(FName SlotName) { return LoadInfo(SlotName); } @@ -302,7 +298,11 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintPure, Category = "SaveExtension|Slots") bool IsSlotSavedById(int32 SlotId) const { - return IsValidSlot(SlotId) ? IsSlotSaved(GetFileNameFromId(SlotId)) : false; + if (ActiveSlot && ActiveSlot->IsValidIndex(SlotId)) + { + return IsSlotSaved(GetFileNameFromId(SlotId)); + } + return false; } /** Check if currently playing in a saved slot @@ -314,23 +314,21 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi return ActiveSlot != nullptr; } - void AssureActiveSlot(bool bForced = false); + void AssureActiveSlot(TSubclassOf ActiveSlotClass = {}, bool bForced = false); UFUNCTION(BlueprintPure, Category = "SaveExtension") FName GetFileNameFromId(const int32 SlotId) const; - bool IsValidSlot(const int32 Slot) const; - void __SetActiveSlot(USaveSlot* NewInfo) + void AssignActiveSlot(USaveSlot* NewInfo) { ActiveSlot = NewInfo; } - USaveSlot* CreateInfo(); USaveSlot* LoadInfo(FName FileName); USaveSlot* LoadInfo(uint32 SlotId) { - return IsValidSlot(SlotId) ? LoadInfo(GetFileNameFromId(SlotId)) : nullptr; + return LoadInfo(GetFileNameFromId(SlotId)); } protected: @@ -453,7 +451,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi inline bool USaveManager::SaveSlot( int32 SlotId, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { - if (!IsValidSlot(SlotId)) + if (!ActiveSlot->IsValidIndex(SlotId)) { UE_LOG(LogSaveExtension, Error, TEXT("Can't save to slot id under 0 or exceeding MaxSlots.")); return false; @@ -461,20 +459,20 @@ inline bool USaveManager::SaveSlot( return SaveSlot(GetFileNameFromId(SlotId), bOverrideIfNeeded, bScreenshot, Size, OnSaved); } -inline bool USaveManager::SaveSlot(const USaveSlot* SlotInfo, bool bOverrideIfNeeded, bool bScreenshot, +inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { - if (!SlotInfo) + if (!Slot) { return false; } - return SaveSlot(SlotInfo->FileName, bOverrideIfNeeded, bScreenshot, Size, OnSaved); + return SaveSlot(Slot->FileName, bOverrideIfNeeded, bScreenshot, Size, OnSaved); } inline void USaveManager::BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { - if (!IsValidSlot(SlotId)) + if (!ActiveSlot || !ActiveSlot->IsValidIndex(SlotId)) { UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Cant go under 0 or exceed MaxSlots.")); Result = ESaveGameResult::Failed; @@ -483,16 +481,16 @@ inline void USaveManager::BPSaveSlotById(int32 SlotId, bool bScreenshot, const F BPSaveSlot(GetFileNameFromId(SlotId), bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } -inline void USaveManager::BPSaveSlotByInfo(const USaveSlot* SlotInfo, bool bScreenshot, +inline void USaveManager::BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { - if (!SlotInfo) + if (!Slot) { Result = ESaveGameResult::Failed; return; } - BPSaveSlot(SlotInfo->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); + BPSaveSlot(Slot->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } /** Save the currently loaded Slot */ @@ -503,7 +501,7 @@ inline bool USaveManager::SaveCurrentSlot(bool bScreenshot, const FScreenshotSiz inline bool USaveManager::LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded) { - if (!IsValidSlot(SlotId)) + if (!ActiveSlot->IsValidIndex(SlotId)) { UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Can't go under 0 or exceed MaxSlots.")); return false; @@ -511,13 +509,13 @@ inline bool USaveManager::LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded) return LoadSlot(GetFileNameFromId(SlotId), OnLoaded); } -inline bool USaveManager::LoadSlot(const USaveSlot* SlotInfo, FOnGameLoaded OnLoaded) +inline bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded) { - if (!SlotInfo) + if (!Slot) { return false; } - return LoadSlot(SlotInfo->FileName, OnLoaded); + return LoadSlot(Slot->FileName, OnLoaded); } inline void USaveManager::BPLoadSlotById( @@ -527,14 +525,14 @@ inline void USaveManager::BPLoadSlotById( } inline void USaveManager::BPLoadSlotByInfo( - const USaveSlot* SlotInfo, ELoadGameResult& Result, FLatentActionInfo LatentInfo) + const USaveSlot* Slot, ELoadGameResult& Result, FLatentActionInfo LatentInfo) { - if (!SlotInfo) + if (!Slot) { Result = ELoadGameResult::Failed; return; } - BPLoadSlot(SlotInfo->FileName, Result, MoveTemp(LatentInfo)); + BPLoadSlot(Slot->FileName, Result, MoveTemp(LatentInfo)); } inline void USaveManager::IterateSubscribedInterfaces(TFunction&& Callback) diff --git a/Source/SaveExtension/Public/SavePreset.h b/Source/SaveExtension/Public/SavePreset.h deleted file mode 100644 index da71d88..0000000 --- a/Source/SaveExtension/Public/SavePreset.h +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Engine/DataAsset.h" -#include "Misc/ClassFilter.h" -#include "UObject/NoExportTypes.h" - -#include "SavePreset.generated.h" - - -/** - * Specifies the behavior while saving or loading - */ -UENUM() -enum class ESaveASyncMode : uint8 -{ - OnlySync, - LoadAsync, - SaveAsync, - SaveAndLoadAsync -}; - -class USaveSlot; -class USaveSlotData; - - -/** - * What to save, how to save it, when, every x minutes, what info file, what data file, save by level - * streaming? - */ -UCLASS(ClassGroup = SaveExtension, Blueprintable) -class SAVEEXTENSION_API USavePreset : public UObject -{ - GENERATED_BODY() - -public: - /** - * If checked, will print messages to Log, and Viewport if DebugInScreen is enabled. - * Ignored in package or Shipping mode. - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay, AdvancedDisplay) - bool bDebug = false; - - /** - * If checked and Debug is enabled, will print messages to Viewport. - * Ignored in package or Shipping mode. - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay, AdvancedDisplay, - meta = (EditCondition = "bDebug")) - bool bDebugInScreen = true; - - - /** If true save files will be compressed - * Performance: Can add from 10ms to 20ms to loading and saving (estimate) but reduce file sizes making - * them up to 30x smaller - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Serialization) - bool bUseCompression = true; - - /** If true will store the game instance */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Serialization) - bool bStoreGameInstance = true; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors") - FSEActorClassFilter ActorFilter; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors", - meta = (PinHiddenByDefault, InlineEditConditionToggle)) - bool bUseLoadActorFilter = false; - - /** If enabled, this filter will be used while loading instead of "ActorFilter" */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Actors", - meta = (EditCondition = "bUseLoadActorFilter")) - FSEActorClassFilter LoadActorFilter; - - /** If true will store ActorComponents depending on the filters */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components") - bool bStoreComponents = true; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components") - FSEComponentClassFilter ComponentFilter; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components", - meta = (PinHiddenByDefault, InlineEditConditionToggle)) - bool bUseLoadComponentFilter = false; - - /** If enabled, this filter will be used while loading instead of "ComponentFilter" */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Serialization|Components", - meta = (EditCondition = "bUseLoadComponentFilter")) - FSEComponentClassFilter LoadComponentFilter; - -public: - /** Serialization will be multi-threaded between all available cores. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous") - ESaveASyncMode MultithreadedSerialization = ESaveASyncMode::SaveAsync; - - /** Split serialization between multiple frames. Ignored if MultithreadedSerialization is used - * Currently only implemented on Loading - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous") - ESaveASyncMode FrameSplittedSerialization = ESaveASyncMode::OnlySync; - - - /** Max milliseconds to use every frame in an asynchronous operation. - * If running at 60Fps (16.6ms), loading or saving asynchronously will add MaxFrameMS: - * 16.6ms + 5MS = 21.6ms -> 46Fps - * This means gameplay will not be stopped nor have frame drops while saving or loading. Works best for - * non multi-threaded platforms - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous", meta = (UIMin = "3", UIMax = "10")) - float MaxFrameMs = 5.f; - - /** Files will be loaded or saved on a secondary thread while game continues */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Asynchronous") - ESaveASyncMode MultithreadedFiles = ESaveASyncMode::SaveAndLoadAsync; - - -protected: - /** If true, will Save and Load levels when they are shown or hidden. - * This includes level streaming and world composition. - */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Level Streaming") - bool bSaveAndLoadSublevels = true; - - -public: - USavePreset(); - - UFUNCTION(BlueprintNativeEvent, Category = "Slots", meta = (DisplayName = "Get SlotName from Id")) - void BPGetSlotNameFromId(int32 Id, FName& Name) const; - - UFUNCTION(BlueprintNativeEvent, Category = "Slots", meta = (DisplayName = "Select AutoLoad file name")) - FName BPGetAutoLoadFileName() const; - -protected: - virtual void GetSlotNameFromId(int32 Id, FName& Name) const; - virtual FName GetAutoLoadFileName() const; - - - /** HELPERS */ -public: - UFUNCTION(BlueprintPure, Category = SavePreset) - FSEActorClassFilter& GetActorFilter(bool bIsLoading) - { - return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; - } - - const FSEActorClassFilter& GetActorFilter(bool bIsLoading) const - { - return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; - } - - UFUNCTION(BlueprintPure, Category = SavePreset) - FSEComponentClassFilter& GetComponentFilter(bool bIsLoading) - { - return (bIsLoading && bUseLoadComponentFilter) ? LoadComponentFilter : ComponentFilter; - } - - const FSEComponentClassFilter& GetComponentFilter(bool bIsLoading) const - { - return (bIsLoading && bUseLoadActorFilter) ? LoadComponentFilter : ComponentFilter; - } - - bool IsMTSerializationLoad() const - { - return MultithreadedSerialization == ESaveASyncMode::LoadAsync || - MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; - } - bool IsMTSerializationSave() const - { - return MultithreadedSerialization == ESaveASyncMode::SaveAsync || - MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; - } - - ESaveASyncMode GetFrameSplitSerialization() const - { - return FrameSplittedSerialization; - } - float GetMaxFrameMs() const - { - return MaxFrameMs; - } - - bool IsFrameSplitLoad() const - { - return !IsMTSerializationLoad() && - (FrameSplittedSerialization == ESaveASyncMode::LoadAsync || - FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); - } - bool IsFrameSplitSave() const - { - return !IsMTSerializationSave() && - (FrameSplittedSerialization == ESaveASyncMode::SaveAsync || - FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); - } - - bool IsMTFilesLoad() const - { - return MultithreadedFiles == ESaveASyncMode::LoadAsync || - MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; - } - bool IsMTFilesSave() const - { - return MultithreadedFiles == ESaveASyncMode::SaveAsync || - MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; - } - - struct FSELevelFilter ToFilter() const; -}; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index a82428b..4dd2812 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -10,6 +10,21 @@ #include "SaveSlot.generated.h" +struct FSELevelFilter; + + +/** + * Specifies the behavior while saving or loading + */ +UENUM() +enum class ESaveASyncMode : uint8 +{ + OnlySync, + LoadAsync, + SaveAsync, + SaveAndLoadAsync +}; + USTRUCT(Blueprintable) struct FSaveSlotStats @@ -17,19 +32,19 @@ struct FSaveSlotStats GENERATED_BODY() /** Played time since this saved game was started. Not related to slots, slots can change */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + UPROPERTY(BlueprintReadOnly, Category = Slot) FTimespan PlayedTime = FTimespan::Zero(); /** Played time since this saved game was created */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + UPROPERTY(BlueprintReadOnly, Category = Slot) FTimespan SlotPlayedTime = FTimespan::Zero(); /** Last date at which this slot was saved. */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + UPROPERTY(BlueprintReadOnly, Category = Slot) FDateTime SaveDate = FDateTime::Now(); /** Date at which this slot was loaded. */ - UPROPERTY(BlueprintReadOnly, Transient, Category = SlotInfo) + UPROPERTY(BlueprintReadOnly, Transient, Category = Slot) FDateTime LoadDate; }; @@ -45,47 +60,133 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame { GENERATED_BODY() -protected: +public: /** Begin Settings */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SlotInfo|Settings") + + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings") TSubclassOf DataClass = USaveSlotData::StaticClass(); /** Maximum amount of saved slots that use this class. 0 is infinite (~16000) */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SlotInfo|Settings", meta = (ClampMin = "0")) + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings", meta = (ClampMin = "0")) int32 MaxSlots = 0; /** If checked, will attempt to Save Game to first Slot found, timed event. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") bool bPeriodicSave = true; /** Interval in seconds for auto saving */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings", + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings", meta = (EditCondition = "bPeriodicSave", UIMin = "15", UIMax = "3600")) int32 PeriodicSaveInterval = 120.f; /** If checked, will attempt to Save Game to current Slot found, timed event. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") bool bSaveOnClose = false; /** If checked, will attempt to Load Game from last Slot found, when game starts */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SlotInfo|Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") bool bLoadOnStart = true; + + /** If true save files will be compressed + * Performance: Can add from 10ms to 20ms to loading and saving (estimate) but reduce file sizes making + * them up to 30x smaller + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") + bool bUseCompression = true; + + /** If true will store the game instance */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Serialization) + bool bStoreGameInstance = true; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") + FSEActorClassFilter ActorFilter; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", + meta = (PinHiddenByDefault, InlineEditConditionToggle)) + bool bUseLoadActorFilter = false; + + /** If enabled, this filter will be used while loading instead of "ActorFilter" */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", + meta = (EditCondition = "bUseLoadActorFilter")) + FSEActorClassFilter LoadActorFilter; + + /** If true will store ActorComponents depending on the filters */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") + bool bStoreComponents = true; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") + FSEComponentClassFilter ComponentFilter; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", + meta = (PinHiddenByDefault, InlineEditConditionToggle)) + bool bUseLoadComponentFilter = false; + + /** If enabled, this filter will be used while loading instead of "ComponentFilter" */ + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", + meta = (EditCondition = "bUseLoadComponentFilter")) + FSEComponentClassFilter LoadComponentFilter; + + /** If true, will Save and Load levels when they are shown or hidden. + * This includes level streaming and world composition. + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Level Streaming") + bool bSaveAndLoadSublevels = true; + + /** Serialization will be multi-threaded between all available cores. */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") + ESaveASyncMode MultithreadedSerialization = ESaveASyncMode::SaveAsync; + + /** Split serialization between multiple frames. Ignored if MultithreadedSerialization is used + * Currently only implemented on Loading + */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") + ESaveASyncMode FrameSplittedSerialization = ESaveASyncMode::OnlySync; + + /** Max milliseconds to use every frame in an asynchronous operation. + * If running at 60Fps (16.6ms), loading or saving asynchronously will add MaxFrameMS: + * 16.6ms + 5MS = 21.6ms -> 46Fps + * This means gameplay will not be stopped nor have frame drops while saving or loading. Works best for + * non multi-threaded platforms + */ + UPROPERTY( + EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async", meta = (UIMin = "3", UIMax = "10")) + float MaxFrameMs = 5.f; + + /** Files will be loaded or saved on a secondary thread while game continues */ + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") + ESaveASyncMode MultithreadedFiles = ESaveASyncMode::SaveAndLoadAsync; + + /** + * If checked, will print messages to Log, and Viewport if DebugInScreen is enabled. + * Ignored in package or Shipping mode. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings|Debug", AdvancedDisplay) + bool bDebug = false; + + /** + * If checked and Debug is enabled, will print messages to Viewport. + * Ignored in package or Shipping mode. + */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings|Debug", AdvancedDisplay, + meta = (EditCondition = "bDebug")) + bool bDebugInScreen = true; + /** End Settings */ public: /** Slot where this SaveInfo and its saveData are saved */ - UPROPERTY(BlueprintReadWrite, Category = SlotInfo) + UPROPERTY(BlueprintReadWrite, Category = Slot) FName FileName = TEXT("Default"); - UPROPERTY(BlueprintReadWrite, Category = SlotInfo) + UPROPERTY(BlueprintReadWrite, Category = Slot) FText DisplayName; /** Root Level where this Slot was saved */ - UPROPERTY(BlueprintReadOnly, Category = SlotInfo) + UPROPERTY(BlueprintReadOnly, Category = Slot) FName Map; - UPROPERTY(BlueprintReadWrite, Category = SlotInfo) + UPROPERTY(BlueprintReadWrite, Category = Slot) FSaveSlotStats Stats; protected: @@ -96,7 +197,7 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame UPROPERTY(Transient) TObjectPtr CachedThumbnail; - UPROPERTY(BlueprintReadOnly, Transient, Category = SlotInfo) + UPROPERTY(BlueprintReadOnly, Transient, Category = Slot) TObjectPtr Data; @@ -105,7 +206,7 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame /** Returns this slot's thumbnail if any */ - UFUNCTION(BlueprintCallable, Category = SlotInfo) + UFUNCTION(BlueprintCallable, Category = Slot) UTexture2D* GetThumbnail() const; /** Captures a thumbnail for the current slot */ @@ -123,11 +224,11 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame Data = NewData; } - UFUNCTION(BlueprintPure, Category = SlotInfo) - int32 GetMaxIds() const; + UFUNCTION(BlueprintPure, Category = Slot) + int32 GetMaxIndexes() const; - UFUNCTION(BlueprintPure, Category = SlotInfo) - bool IsValidId(int32 CheckedId) const; + UFUNCTION(BlueprintPure, Category = Slot) + bool IsValidIndex(int32 Index) const; /** Internal Usage. Will be called when an screenshot is captured */ void _SetThumbnailPath(const FString& Path); @@ -139,13 +240,34 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame } public: - UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = SlotInfo) - bool SetId(int32 Id); + UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Slot) + bool SetIndex(int32 Index); - UFUNCTION(BlueprintPure, BlueprintImplementableEvent, Category = SlotInfo) - int32 GetId() const; + UFUNCTION(BlueprintPure, BlueprintNativeEvent, Category = Slot) + int32 GetIndex() const; protected: - virtual bool OnSetId(int32 Id); - virtual int32 OnGetId() const; + virtual bool OnSetIndex(int32 Index); + virtual int32 OnGetIndex() const; + +public: + UFUNCTION(BlueprintPure, Category = SaveSlot) + const FSEActorClassFilter& GetActorFilter(bool bIsLoading) const; + + UFUNCTION(BlueprintPure, Category = SaveSlot) + const FSEComponentClassFilter& GetComponentFilter(bool bIsLoading) const; + + bool IsMTSerializationLoad() const; + bool IsMTSerializationSave() const; + + ESaveASyncMode GetFrameSplitSerialization() const; + float GetMaxFrameMs() const; + + bool IsFrameSplitLoad() const; + bool IsFrameSplitSave() const; + + bool IsMTFilesLoad() const; + bool IsMTFilesSave() const; + + FSELevelFilter ToFilter() const; }; diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index 5a167f0..bd60669 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -3,7 +3,6 @@ #pragma once -#include "ISaveExtension.h" #include "Serialization/LevelRecords.h" #include "Serialization/Records.h" @@ -46,7 +45,7 @@ class SAVEEXTENSION_API USaveSlotData : public USaveGame bool bStoreGameInstance = false; FObjectRecord GameInstance; - FSELevelFilter GeneralLevelFilter; + FSELevelFilter GlobalLevelFilter; FPersistentLevelRecord MainLevel; TArray SubLevels; diff --git a/Source/SaveExtension/Public/Serialization/LevelRecords.h b/Source/SaveExtension/Public/Serialization/LevelRecords.h index b892aef..9be4385 100644 --- a/Source/SaveExtension/Public/Serialization/LevelRecords.h +++ b/Source/SaveExtension/Public/Serialization/LevelRecords.h @@ -13,15 +13,14 @@ #include "LevelRecords.generated.h" - /** Represents a level in the world (streaming or persistent) */ USTRUCT() struct FLevelRecord : public FBaseRecord { GENERATED_BODY() - bool bOverrideGeneralFilter = false; - // Filter is used if bOverrideGeneralFilter is true + bool bOverrideGlobalFilter = false; + // Filter is used if bOverrideGlobalFilter is true FSELevelFilter Filter; /** Record of the Level Script Actor */ diff --git a/Source/SaveExtension/Public/Serialization/MTTask.h b/Source/SaveExtension/Public/Serialization/MTTask.h index cf4c233..2f19814 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask.h +++ b/Source/SaveExtension/Public/Serialization/MTTask.h @@ -4,7 +4,6 @@ #include "ISaveExtension.h" #include "LevelFilter.h" -#include "SavePreset.h" #include #include diff --git a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h index d3b0bf6..03de370 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h +++ b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h @@ -4,7 +4,6 @@ #include "MTTask.h" #include "MTTask_SerializeActors.h" -#include "SavePreset.h" #include "Serialization/LevelRecords.h" #include "Serialization/Records.h" @@ -16,8 +15,9 @@ class USaveSlotData; + /** Called when game has been saved - * @param SlotInfo the saved slot. Null if save failed + * @param Slot the saved slot. Null if save failed */ DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask.h b/Source/SaveExtension/Public/Serialization/SlotDataTask.h index a06a4f8..8e8172f 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask.h @@ -33,20 +33,16 @@ class USaveSlotDataTask : public UObject UPROPERTY() USaveSlotData* SlotData; - UPROPERTY() - const USavePreset* Preset; - UPROPERTY() float MaxFrameMs = 0.f; public: USaveSlotDataTask() : Super(), bRunning(false), bFinished(false) {} - void Prepare(USaveSlotData* InSaveData, const USavePreset& InPreset) + void Prepare(USaveSlot* Slot) { - SlotData = InSaveData; - Preset = &InPreset; - MaxFrameMs = Preset->GetMaxFrameMs(); + SlotData = Slot->GetData(); + MaxFrameMs = Slot->GetMaxFrameMs(); } USaveSlotDataTask* Start(); @@ -84,7 +80,7 @@ class USaveSlotDataTask : public UObject void BakeAllFilters(); - const FSELevelFilter& GetGeneralFilter() const; + const FSELevelFilter& GetGlobalFilter() const; const FSELevelFilter& GetLevelFilter(const FLevelRecord& Level) const; FLevelRecord* FindLevelRecord(const ULevelStreaming* Level) const; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h index ad95e3c..dbb7a13 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h @@ -3,7 +3,6 @@ #pragma once #include "ISaveExtension.h" -#include "SavePreset.h" #include "SlotDataTask_Loader.h" #include "SlotDataTask_LevelLoader.generated.h" diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h index bd792f9..8477509 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h @@ -3,7 +3,6 @@ #pragma once #include "ISaveExtension.h" -#include "SavePreset.h" #include "SlotDataTask_Saver.h" #include "SlotDataTask_LevelSaver.generated.h" @@ -32,6 +31,6 @@ class USaveSlotDataTask_LevelSaver : public USaveSlotDataTask_Saver virtual void OnStart() override; virtual void OnFinish(bool bSuccess) override { - SELog(Preset, "Finished Serializing level", FColor::Green); + SELog(Slot, "Finished Serializing level", FColor::Green); } }; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h index 8e6b99a..39c27ce 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h @@ -5,7 +5,6 @@ #include "Delegates.h" #include "ISaveExtension.h" #include "Multithreading/LoadFileTask.h" -#include "SavePreset.h" #include "SaveSlot.h" #include "SaveSlotData.h" #include "SlotDataTask.h" @@ -39,10 +38,11 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask { GENERATED_BODY() +protected: FName SlotName; UPROPERTY() - USaveSlot* NewSlotInfo; + USaveSlot* Slot; FOnGameLoaded Delegate; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h b/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h index a11a368..529ae76 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h +++ b/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h @@ -6,7 +6,6 @@ #include "ISaveExtension.h" #include "MTTask_SerializeActors.h" #include "Multithreading/SaveFileTask.h" -#include "SavePreset.h" #include "SaveSlotData.h" #include "SlotDataTask.h" @@ -39,7 +38,7 @@ class USaveSlotDataTask_Saver : public USaveSlotDataTask protected: UPROPERTY() - USaveSlot* SlotInfo; + USaveSlot* Slot; /** Start Async variables */ TWeakObjectPtr CurrentLevel; diff --git a/Source/Test/Private/Files.spec.cpp b/Source/Test/Private/Files.spec.cpp index 7094c90..37f281f 100644 --- a/Source/Test/Private/Files.spec.cpp +++ b/Source/Test/Private/Files.spec.cpp @@ -1,9 +1,10 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. #include "Automatron.h" -#include "FileAdapter.h" #include "Helpers/TestActor.h" -#include "SaveManager.h" + +#include +#include class FSaveSpec_Files : public Automatron::FTestSpec @@ -13,7 +14,6 @@ class FSaveSpec_Files : public Automatron::FTestSpec USaveManager* SaveManager = nullptr; ATestActor* TestActor = nullptr; - USavePreset* TestPreset = nullptr; // Helper for some test delegates bool bFinishTick = false; @@ -34,33 +34,31 @@ void FSaveSpec_Files::Define() SaveManager->bTickWithGameWorld = true; - // Set test preset - TestPreset = SaveManager->SetActivePreset(USavePreset::StaticClass()); - TestPreset->MultithreadedSerialization = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedSerialization = ESaveASyncMode::OnlySync; }); It("Can save files synchronously", [this]() { - TestPreset->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::OnlySync; TestTrue("Saved", SaveManager->SaveSlot(0)); - TestTrue("Info File exists in disk", FFileAdapter::FileExists(TEXT("0"))); + TestTrue("Info File exists in disk", FSaveFileHelpers::FileExists(TEXT("0"))); }); It("Can save files asynchronously", [this]() { - TestPreset->MultithreadedFiles = ESaveASyncMode::SaveAsync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::SaveAsync; bFinishTick = false; bool bSaving = SaveManager->SaveSlot(0, true, false, {}, FOnGameSaved::CreateLambda([this](auto* Info) { // Notified that files have been saved asynchronously - TestTrue("Info File exists in disk", FFileAdapter::FileExists(TEXT("0"))); + TestTrue("Info File exists in disk", FSaveFileHelpers::FileExists(TEXT("0"))); bFinishTick = true; })); TestTrue("Started Saving", bSaving); // Files shouldn't exist yet - TestFalse("Info File exists in disk", FFileAdapter::FileExists(TEXT("0"))); + TestFalse("Info File exists in disk", FSaveFileHelpers::FileExists(TEXT("0"))); TickWorldUntil(GetMainWorld(), true, [this](float) { return !bFinishTick; @@ -68,12 +66,12 @@ void FSaveSpec_Files::Define() }); It("Can load files synchronously", [this]() { - TestPreset->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::OnlySync; TestTrue("Saved", SaveManager->SaveSlot(0)); USaveSlot* Slot = nullptr; - TestNotNull("File was loaded", FFileAdapter::LoadFile(TEXT("0"), Slot, true, SaveManager)); + TestTrue("File was loaded", FSaveFileHelpers::LoadFile(TEXT("0"), Slot, true, SaveManager)); TestNotNull("Info is valid", Slot); TestNotNull("Data is valid", Slot->GetData()); }); diff --git a/Source/Test/Private/GameInstance.spec.cpp b/Source/Test/Private/GameInstanceSpec.cpp similarity index 82% rename from Source/Test/Private/GameInstance.spec.cpp rename to Source/Test/Private/GameInstanceSpec.cpp index eed461d..522fc53 100644 --- a/Source/Test/Private/GameInstance.spec.cpp +++ b/Source/Test/Private/GameInstanceSpec.cpp @@ -1,7 +1,8 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. +#include "GameInstanceSpec.h" + #include "Automatron.h" -#include "Helpers/TestGameInstance.h" #include "SaveManager.h" @@ -11,7 +12,6 @@ class FSaveSpec_GameInstance : public Automatron::FTestSpec EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter); USaveManager* SaveManager = nullptr; - USavePreset* TestPreset = nullptr; // Helper for some test delegates bool bFinishTick = false; @@ -34,11 +34,7 @@ void FSaveSpec_GameInstance::Define() SaveManager->bTickWithGameWorld = true; - TestPreset = SaveManager->SetActivePreset(USavePreset::StaticClass()); - TestPreset->bStoreGameInstance = true; - - TestPreset->MultithreadedFiles = ESaveASyncMode::OnlySync; - TestPreset->MultithreadedSerialization = ESaveASyncMode::OnlySync; + SaveManager->AssureActiveSlot(UTestSaveSlot::StaticClass(), true); }); It("GameInstance can be saved", [this]() { diff --git a/Source/Test/Private/GameInstanceSpec.h b/Source/Test/Private/GameInstanceSpec.h new file mode 100644 index 0000000..92e7fca --- /dev/null +++ b/Source/Test/Private/GameInstanceSpec.h @@ -0,0 +1,23 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. +#pragma once + +#include "Helpers/TestGameInstance.h" + +#include + +#include "GameInstanceSpec.generated.h" + + +UCLASS() +class UTestSaveSlot : public USaveSlot +{ + GENERATED_BODY() + + UTestSaveSlot() : Super() + { + bStoreGameInstance = true; + + MultithreadedFiles = ESaveASyncMode::OnlySync; + MultithreadedSerialization = ESaveASyncMode::OnlySync; + } +}; diff --git a/Source/Test/Private/Save.spec.cpp b/Source/Test/Private/SavingSpec.cpp similarity index 89% rename from Source/Test/Private/Save.spec.cpp rename to Source/Test/Private/SavingSpec.cpp index b55fcc4..f84a5f3 100644 --- a/Source/Test/Private/Save.spec.cpp +++ b/Source/Test/Private/SavingSpec.cpp @@ -1,7 +1,8 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. +#include "SavingSpec.h" + #include "Automatron.h" -#include "Helpers/TestActor.h" #include "SaveManager.h" @@ -12,7 +13,6 @@ class FSaveSpec_Preset : public Automatron::FTestSpec USaveManager* SaveManager = nullptr; ATestActor* TestActor = nullptr; - USavePreset* TestPreset = nullptr; // Helper for some test delegates bool bFinishTick = false; @@ -57,18 +57,12 @@ void FSaveSpec_Preset::Define() Describe("Serialization", [this]() { BeforeEach([this]() { - TestPreset = SaveManager->SetActivePreset(USavePreset::StaticClass()); - TestPreset->ActorFilter.ClassFilter.AllowedClasses.Add(ATestActor::StaticClass()); - - // We don't need Async files are tested independently - TestPreset->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->AssureActiveSlot(UTestSaveSlot_SyncSaving::StaticClass(), true); TestActor = GetMainWorld()->SpawnActor(); }); It("Can save an actor synchronously", [this]() { - TestPreset->MultithreadedSerialization = ESaveASyncMode::OnlySync; - TestTrue("Saved", SaveManager->SaveSlot(0)); TestTrue("Loaded", SaveManager->LoadSlot(0)); }); @@ -78,10 +72,6 @@ void FSaveSpec_Preset::Define() }); Describe("Properties", [this]() { - BeforeEach([this]() { - TestPreset->MultithreadedSerialization = ESaveASyncMode::OnlySync; - }); - It("bool", [this]() { TestActor->bMyBool = true; SaveManager->SaveSlot(0); diff --git a/Source/Test/Private/SavingSpec.h b/Source/Test/Private/SavingSpec.h new file mode 100644 index 0000000..f16b5e0 --- /dev/null +++ b/Source/Test/Private/SavingSpec.h @@ -0,0 +1,24 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. +#pragma once + +#include "Helpers/TestActor.h" + +#include + +#include "SavingSpec.generated.h" + + +UCLASS() +class UTestSaveSlot_SyncSaving : public USaveSlot +{ + GENERATED_BODY() + + UTestSaveSlot_SyncSaving() : Super() + { + bStoreGameInstance = true; + + MultithreadedFiles = ESaveASyncMode::OnlySync; + MultithreadedSerialization = ESaveASyncMode::OnlySync; + ActorFilter.ClassFilter.AllowedClasses.Add(ATestActor::StaticClass()); + } +}; From 1690194b1c559f01173480ff99cf24f18791c70d Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 20 Sep 2023 17:56:36 +0200 Subject: [PATCH 3/7] Refactored blueprint actions --- .../LatentActions/DeleteSlotsAction.cpp | 25 --- .../Private/LatentActions/LoadGameAction.cpp | 34 ---- .../Private/LatentActions/LoadInfosAction.cpp | 28 --- .../Private/LatentActions/SaveGameAction.cpp | 35 ---- Source/SaveExtension/Private/SaveManager.cpp | 179 ++++++++++++++++-- .../Public/LatentActions/DeleteSlotsAction.h | 50 ----- .../Public/LatentActions/LoadGameAction.h | 49 ----- .../Public/LatentActions/LoadInfosAction.h | 52 ----- .../Public/LatentActions/SaveGameAction.h | 50 ----- Source/SaveExtension/Public/SaveManager.h | 60 +++--- 10 files changed, 201 insertions(+), 361 deletions(-) delete mode 100644 Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp delete mode 100644 Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp delete mode 100644 Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp delete mode 100644 Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp delete mode 100644 Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h delete mode 100644 Source/SaveExtension/Public/LatentActions/LoadGameAction.h delete mode 100644 Source/SaveExtension/Public/LatentActions/LoadInfosAction.h delete mode 100644 Source/SaveExtension/Public/LatentActions/SaveGameAction.h diff --git a/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp b/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp deleted file mode 100644 index b600bbe..0000000 --- a/Source/SaveExtension/Private/LatentActions/DeleteSlotsAction.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "LatentActions/DeleteSlotsAction.h" - -#include "SaveManager.h" -#include "SaveSlot.h" - - -FDeleteSlotsAction::FDeleteSlotsAction( - USaveManager* Manager, EDeleteSlotsResult& OutResult, const FLatentActionInfo& LatentInfo) - : Result(OutResult) - , bFinished(false) - , ExecutionFunction(LatentInfo.ExecutionFunction) - , OutputLink(LatentInfo.Linkage) - , CallbackTarget(LatentInfo.CallbackTarget) -{ - Manager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() { - bFinished = true; - })); -} - -void FDeleteSlotsAction::UpdateOperation(FLatentResponse& Response) -{ - Response.FinishAndTriggerIf(bFinished, ExecutionFunction, OutputLink, CallbackTarget); -} diff --git a/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp b/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp deleted file mode 100644 index da32ab2..0000000 --- a/Source/SaveExtension/Private/LatentActions/LoadGameAction.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "LatentActions/LoadGameAction.h" - -#include "SaveManager.h" -#include "SaveSlot.h" -#include "Serialization/SlotDataTask_Loader.h" - - -FLoadGameAction::FLoadGameAction( - USaveManager* Manager, FName SlotName, ELoadGameResult& OutResult, const FLatentActionInfo& LatentInfo) - : Result(OutResult) - , ExecutionFunction(LatentInfo.ExecutionFunction) - , OutputLink(LatentInfo.Linkage) - , CallbackTarget(LatentInfo.CallbackTarget) -{ - const bool bStarted = - Manager->LoadSlot(SlotName, FOnGameLoaded::CreateRaw(this, &FLoadGameAction::OnLoadFinished)); - if (!bStarted) - { - Result = ELoadGameResult::Failed; - } -} - -void FLoadGameAction::UpdateOperation(FLatentResponse& Response) -{ - Response.FinishAndTriggerIf( - Result != ELoadGameResult::Loading, ExecutionFunction, OutputLink, CallbackTarget); -} - -void FLoadGameAction::OnLoadFinished(USaveSlot* SavedSlot) -{ - Result = SavedSlot ? ELoadGameResult::Continue : ELoadGameResult::Failed; -} diff --git a/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp b/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp deleted file mode 100644 index 4b989e8..0000000 --- a/Source/SaveExtension/Private/LatentActions/LoadInfosAction.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "LatentActions/LoadInfosAction.h" - -#include "SaveManager.h" -#include "SaveSlot.h" - - -FLoadInfosAction::FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, - TArray& InSlots, ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo) - : Result(OutResult) - , Slots(InSlots) - , bFinished(false) - , ExecutionFunction(LatentInfo.ExecutionFunction) - , OutputLink(LatentInfo.Linkage) - , CallbackTarget(LatentInfo.CallbackTarget) -{ - Manager->FindAllSlots( - bSortByRecent, FOnSlotsLoaded::CreateLambda([this](const TArray& Results) { - Slots = Results; - bFinished = true; - })); -} - -void FLoadInfosAction::UpdateOperation(FLatentResponse& Response) -{ - Response.FinishAndTriggerIf(bFinished, ExecutionFunction, OutputLink, CallbackTarget); -} diff --git a/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp b/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp deleted file mode 100644 index 1746e33..0000000 --- a/Source/SaveExtension/Private/LatentActions/SaveGameAction.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "LatentActions/SaveGameAction.h" - -#include "SaveManager.h" -#include "SaveSlot.h" - - -FSaveGameAction::FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, - bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& OutResult, - const FLatentActionInfo& LatentInfo) - : Result(OutResult) - , ExecutionFunction(LatentInfo.ExecutionFunction) - , OutputLink(LatentInfo.Linkage) - , CallbackTarget(LatentInfo.CallbackTarget) -{ - const bool bStarted = Manager->SaveSlot(SlotName, bOverrideIfNeeded, bScreenshot, Size, - FOnGameSaved::CreateRaw(this, &FSaveGameAction::OnSaveFinished)); - - if (!bStarted) - { - Result = ESaveGameResult::Failed; - } -} - -void FSaveGameAction::UpdateOperation(FLatentResponse& Response) -{ - Response.FinishAndTriggerIf( - Result != ESaveGameResult::Saving, ExecutionFunction, OutputLink, CallbackTarget); -} - -void FSaveGameAction::OnSaveFinished(USaveSlot* SavedSlot) -{ - Result = SavedSlot ? ESaveGameResult::Continue : ESaveGameResult::Failed; -} diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index e550599..1acd6d0 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -2,7 +2,6 @@ #include "SaveManager.h" -#include "LatentActions/LoadInfosAction.h" #include "Multithreading/DeleteSlotsTask.h" #include "Multithreading/LoadSlotsTask.h" #include "SaveFileHelpers.h" @@ -13,16 +12,170 @@ #include "Serialization/SlotDataTask_Saver.h" #include +#include #include #include #include #include #include #include +#include #include #include +// BEGIN Async Actions + +class FSELoadSlotDataAction : public FPendingLatentAction +{ +public: + ESEContinueOrFail& Result; + FName ExecutionFunction; + int32 OutputLink; + FWeakObjectPtr CallbackTarget; + + FSELoadSlotDataAction(USaveManager* Manager, FName SlotName, ESEContinueOrFail& OutResult, + const FLatentActionInfo& LatentInfo) + : Result(OutResult) + , ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + { + const bool bStarted = Manager->LoadSlot( + SlotName, FOnGameLoaded::CreateRaw(this, &FSELoadSlotDataAction::OnLoadFinished)); + Result = bStarted ? ESEContinueOrFail::InProgress : ESEContinueOrFail::Failed; + } + void UpdateOperation(FLatentResponse& Response) override + { + Response.FinishAndTriggerIf( + Result != ESEContinueOrFail::InProgress, ExecutionFunction, OutputLink, CallbackTarget); + } + void OnLoadFinished(USaveSlot* SavedSlot) + { + Result = SavedSlot ? ESEContinueOrFail::Continue : ESEContinueOrFail::Failed; + } +#if WITH_EDITOR + // Returns a human readable description of the latent operation's current state + FString GetDescription() const override + { + return TEXT("Loading Game..."); + } +#endif +}; + + +class FDeleteSlotsAction : public FPendingLatentAction +{ +public: + ESEContinue& Result; + FName ExecutionFunction; + int32 OutputLink; + FWeakObjectPtr CallbackTarget; + + FDeleteSlotsAction(USaveManager* Manager, ESEContinue& OutResult, const FLatentActionInfo& LatentInfo) + : Result(OutResult) + , ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + { + Result = ESEContinue::InProgress; + Manager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() { + Result = ESEContinue::Continue; + })); + } + void UpdateOperation(FLatentResponse& Response) override + { + Response.FinishAndTriggerIf( + Result != ESEContinue::InProgress, ExecutionFunction, OutputLink, CallbackTarget); + } +#if WITH_EDITOR + FString GetDescription() const override + { + return TEXT("Deleting all slots..."); + } +#endif +}; + + +class FSELoadInfosAction : public FPendingLatentAction +{ +public: + TArray& Slots; + ESEContinue& Result; + FName ExecutionFunction; + int32 OutputLink; + FWeakObjectPtr CallbackTarget; + + FSELoadInfosAction(USaveManager* Manager, const bool bSortByRecent, TArray& OutSlots, + ESEContinue& OutResult, const FLatentActionInfo& LatentInfo) + : Slots(OutSlots) + , Result(OutResult) + , ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + { + Result = ESEContinue::InProgress; + Manager->FindAllSlots( + bSortByRecent, FOnSlotsLoaded::CreateLambda([this](const TArray& Results) { + Slots = Results; + Result = ESEContinue::Continue; + })); + } + virtual void UpdateOperation(FLatentResponse& Response) override + { + Response.FinishAndTriggerIf( + Result != ESEContinue::InProgress, ExecutionFunction, OutputLink, CallbackTarget); + } +#if WITH_EDITOR + virtual FString GetDescription() const override + { + return TEXT("Loading all slots..."); + } +#endif +}; + + +class FSaveGameAction : public FPendingLatentAction +{ +public: + ESEContinueOrFail& Result; + FName ExecutionFunction; + int32 OutputLink; + FWeakObjectPtr CallbackTarget; + + FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, + const FScreenshotSize Size, ESEContinueOrFail& OutResult, const FLatentActionInfo& LatentInfo) + : Result(OutResult) + , ExecutionFunction(LatentInfo.ExecutionFunction) + , OutputLink(LatentInfo.Linkage) + , CallbackTarget(LatentInfo.CallbackTarget) + { + const bool bStarted = Manager->SaveSlot(SlotName, bOverrideIfNeeded, bScreenshot, Size, + FOnGameSaved::CreateRaw(this, &FSaveGameAction::OnSaveFinished)); + Result = bStarted ? ESEContinueOrFail::InProgress : ESEContinueOrFail::Failed; + } + + virtual void UpdateOperation(FLatentResponse& Response) override + { + Response.FinishAndTriggerIf( + Result != ESEContinueOrFail::InProgress, ExecutionFunction, OutputLink, CallbackTarget); + } + void OnSaveFinished(USaveSlot* SavedSlot) + { + Result = SavedSlot ? ESEContinueOrFail::Continue : ESEContinueOrFail::Failed; + } +#if WITH_EDITOR + // Returns a human readable description of the latent operation's current state + virtual FString GetDescription() const override + { + return TEXT("Saving Game..."); + } +#endif +}; + +// END Async Actions + + USaveManager::USaveManager() : Super(), MTTasks{} {} void USaveManager::Initialize(FSubsystemCollectionBase& Collection) @@ -147,12 +300,10 @@ void USaveManager::DeleteAllSlots(FOnSlotsDeleted Delegate) } void USaveManager::BPSaveSlot(FName SlotName, bool bScreenshot, const FScreenshotSize Size, - ESaveGameResult& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded /*= true*/) + ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded /*= true*/) { if (UWorld* World = GetWorld()) { - Result = ESaveGameResult::Saving; - FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); if (LatentActionManager.FindExistingAction( LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) @@ -163,43 +314,41 @@ void USaveManager::BPSaveSlot(FName SlotName, bool bScreenshot, const FScreensho } return; } - Result = ESaveGameResult::Failed; + Result = ESEContinueOrFail::Failed; } -void USaveManager::BPLoadSlot(FName SlotName, ELoadGameResult& Result, struct FLatentActionInfo LatentInfo) +void USaveManager::BPLoadSlot(FName SlotName, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) { - Result = ELoadGameResult::Loading; - FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); - if (LatentActionManager.FindExistingAction( + if (LatentActionManager.FindExistingAction( LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) { LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, - new FLoadGameAction(this, SlotName, Result, LatentInfo)); + new FSELoadSlotDataAction(this, SlotName, Result, LatentInfo)); } return; } - Result = ELoadGameResult::Failed; + Result = ESEContinueOrFail::Failed; } void USaveManager::BPFindAllSlots(const bool bSortByRecent, TArray& SaveInfos, - ELoadInfoResult& Result, struct FLatentActionInfo LatentInfo) + ESEContinue& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) { FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); - if (LatentActionManager.FindExistingAction( + if (LatentActionManager.FindExistingAction( LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) { LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, - new FLoadInfosAction(this, bSortByRecent, SaveInfos, Result, LatentInfo)); + new FSELoadInfosAction(this, bSortByRecent, SaveInfos, Result, LatentInfo)); } } } -void USaveManager::BPDeleteAllSlots(EDeleteSlotsResult& Result, struct FLatentActionInfo LatentInfo) +void USaveManager::BPDeleteAllSlots(ESEContinue& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) { diff --git a/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h b/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h deleted file mode 100644 index a1c6ffd..0000000 --- a/Source/SaveExtension/Public/LatentActions/DeleteSlotsAction.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include -#include -#include - - -class USaveManager; -class USaveSlot; - -/** - * Enum used to indicate quote execution results - */ -UENUM() -enum class EDeleteSlotsResult : uint8 -{ - Completed -}; - -/** FLoadInfosction */ -class FDeleteSlotsAction : public FPendingLatentAction -{ -public: - EDeleteSlotsResult& Result; - - bool bFinished; - - FName ExecutionFunction; - int32 OutputLink; - FWeakObjectPtr CallbackTarget; - - - /** - * @param SlotId will load that Saved Game if Id > 0, otherwise it will load all infos - */ - FDeleteSlotsAction( - USaveManager* Manager, EDeleteSlotsResult& OutResult, const FLatentActionInfo& LatentInfo); - - virtual void UpdateOperation(FLatentResponse& Response) override; - -#if WITH_EDITOR - // Returns a human readable description of the latent operation's current state - virtual FString GetDescription() const override - { - return TEXT("Deleting all slots..."); - } -#endif -}; diff --git a/Source/SaveExtension/Public/LatentActions/LoadGameAction.h b/Source/SaveExtension/Public/LatentActions/LoadGameAction.h deleted file mode 100644 index cfa1977..0000000 --- a/Source/SaveExtension/Public/LatentActions/LoadGameAction.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include -#include -#include - - -class USaveManager; -class USaveSlot; - -/** - * Enum used to indicate quote execution results - */ -UENUM() -enum class ELoadGameResult : uint8 -{ - Loading UMETA(Hidden), - Continue, - Failed -}; - -/** FLoadGameAction */ -class FLoadGameAction : public FPendingLatentAction -{ -public: - ELoadGameResult& Result; - - FName ExecutionFunction; - int32 OutputLink; - FWeakObjectPtr CallbackTarget; - - - FLoadGameAction( - USaveManager* Manager, FName SlotName, ELoadGameResult& Result, const FLatentActionInfo& LatentInfo); - - virtual void UpdateOperation(FLatentResponse& Response) override; - - void OnLoadFinished(USaveSlot* SavedSlot); - -#if WITH_EDITOR - // Returns a human readable description of the latent operation's current state - virtual FString GetDescription() const override - { - return TEXT("Loading Game..."); - } -#endif -}; diff --git a/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h b/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h deleted file mode 100644 index e855625..0000000 --- a/Source/SaveExtension/Public/LatentActions/LoadInfosAction.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "Multithreading/LoadSlotsTask.h" - -#include -#include -#include - - -class USaveManager; -class USaveSlot; - -/** - * Enum used to indicate quote execution results - */ -UENUM() -enum class ELoadInfoResult : uint8 -{ - Completed -}; - -/** FLoadInfosction */ -class FLoadInfosAction : public FPendingLatentAction -{ -public: - ELoadInfoResult& Result; - - TArray& Slots; - bool bFinished; - - FName ExecutionFunction; - int32 OutputLink; - FWeakObjectPtr CallbackTarget; - - - /** - * @param SlotId will load that Saved Game if Id > 0, otherwise it will load all infos - */ - FLoadInfosAction(USaveManager* Manager, const bool bSortByRecent, TArray& SaveInfos, - ELoadInfoResult& OutResult, const FLatentActionInfo& LatentInfo); - - virtual void UpdateOperation(FLatentResponse& Response) override; - -#if WITH_EDITOR - virtual FString GetDescription() const override - { - return TEXT("Loading all infos..."); - } -#endif -}; diff --git a/Source/SaveExtension/Public/LatentActions/SaveGameAction.h b/Source/SaveExtension/Public/LatentActions/SaveGameAction.h deleted file mode 100644 index e246756..0000000 --- a/Source/SaveExtension/Public/LatentActions/SaveGameAction.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include -#include -#include - - -class USaveManager; -class USaveSlot; -struct FScreenshotSize; - -/** - * Enum used to indicate quote execution results - */ -UENUM() -enum class ESaveGameResult : uint8 -{ - Saving UMETA(Hidden), - Continue, - Failed -}; - -/** FSaveGameAction */ -class FSaveGameAction : public FPendingLatentAction -{ -public: - ESaveGameResult& Result; - - FName ExecutionFunction; - int32 OutputLink; - FWeakObjectPtr CallbackTarget; - - - FSaveGameAction(USaveManager* Manager, FName SlotName, bool bOverrideIfNeeded, bool bScreenshot, - const FScreenshotSize Size, ESaveGameResult& OutResult, const FLatentActionInfo& LatentInfo); - - virtual void UpdateOperation(FLatentResponse& Response) override; - - void OnSaveFinished(USaveSlot* SavedSlot); - -#if WITH_EDITOR - // Returns a human readable description of the latent operation's current state - virtual FString GetDescription() const override - { - return TEXT("Saving Game..."); - } -#endif -}; diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index abd7337..ff663b6 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -3,9 +3,6 @@ #pragma once #include "Delegates.h" -#include "LatentActions/DeleteSlotsAction.h" -#include "LatentActions/LoadGameAction.h" -#include "LatentActions/SaveGameAction.h" #include "LevelStreamingNotifier.h" #include "Multithreading/Delegates.h" #include "Multithreading/ScopedTaskManager.h" @@ -25,11 +22,28 @@ #include "SaveManager.generated.h" +struct FLatentActionInfo; + + DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, Slot); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, Slot); -struct FLatentActionInfo; +UENUM() +enum class ESEContinue : uint8 +{ + InProgress UMETA(Hidden), + Continue +}; + +UENUM() +enum class ESEContinueOrFail : uint8 +{ + InProgress UMETA(Hidden), + Continue, + Failed +}; + USTRUCT(BlueprintType) struct FScreenshotSize @@ -165,7 +179,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPSaveSlot(FName SlotName, bool bScreenshot, - UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game into an specified Slot */ @@ -173,7 +187,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPSaveSlotById(int32 SlotId, bool bScreenshot, - UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game to a Slot */ @@ -181,7 +195,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Info", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreenshot, - UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the currently loaded Slot */ @@ -189,7 +203,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Current Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPSaveCurrentSlot(bool bScreenshot, - UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESaveGameResult& Result, + UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { BPSaveSlotByInfo(ActiveSlot, bScreenshot, Size, Result, MoveTemp(LatentInfo), true); @@ -199,25 +213,25 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", meta = (DisplayName = "Load Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlot(FName SlotName, ELoadGameResult& Result, FLatentActionInfo LatentInfo); + void BPLoadSlot(FName SlotName, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Load game from a slot Id */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", meta = (DisplayName = "Load Slot by Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotById(int32 SlotId, ELoadGameResult& Result, FLatentActionInfo LatentInfo); + void BPLoadSlotById(int32 SlotId, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Load game from a Slot */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", meta = (DisplayName = "Load Slot by Info", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotByInfo(const USaveSlot* Slot, ELoadGameResult& Result, FLatentActionInfo LatentInfo); + void BPLoadSlotByInfo(const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Reload the currently loaded slot if any */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", meta = (DisplayName = "Reload Current Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPReloadCurrentSlot(ELoadGameResult& Result, FLatentActionInfo LatentInfo) + void BPReloadCurrentSlot(ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { BPLoadSlotByInfo(ActiveSlot, Result, MoveTemp(LatentInfo)); } @@ -230,7 +244,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintCallable, Category = "SaveExtension", meta = (Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", DisplayName = "Find All Slots")) - void BPFindAllSlots(const bool bSortByRecent, TArray& Slots, ELoadInfoResult& Result, + void BPFindAllSlots(const bool bSortByRecent, TArray& Slots, ESEContinue& Result, struct FLatentActionInfo LatentInfo); /** Delete a saved game on an specified slot Id @@ -246,7 +260,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintCallable, Category = "SaveExtension", meta = (Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", DisplayName = "Delete All Slots")) - void BPDeleteAllSlots(EDeleteSlotsResult& Result, FLatentActionInfo LatentInfo); + void BPDeleteAllSlots(ESEContinue& Result, FLatentActionInfo LatentInfo); /** BLUEPRINTS & C++ API */ @@ -431,7 +445,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi meta = (DeprecatedFunction, DeprecationMessage = "Use 'Save Slot by Id' instead.", AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot to Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotToId(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESaveGameResult& Result, + void BPSaveSlotToId(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true) { BPSaveSlotById(SlotId, bScreenshot, Size, Result, LatentInfo, bOverrideIfNeeded); @@ -441,7 +455,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi meta = (DeprecatedFunction, DeprecationMessage = "Use 'Load Slot by Id' instead.", DisplayName = "Load Slot from Id", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotFromId(int32 SlotId, ELoadGameResult& Result, FLatentActionInfo LatentInfo) + void BPLoadSlotFromId(int32 SlotId, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { BPLoadSlotById(SlotId, Result, LatentInfo); } @@ -470,24 +484,24 @@ inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded } inline void USaveManager::BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, - ESaveGameResult& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) + ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { if (!ActiveSlot || !ActiveSlot->IsValidIndex(SlotId)) { UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Cant go under 0 or exceed MaxSlots.")); - Result = ESaveGameResult::Failed; + Result = ESEContinueOrFail::Failed; return; } BPSaveSlot(GetFileNameFromId(SlotId), bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } inline void USaveManager::BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreenshot, - const FScreenshotSize Size, ESaveGameResult& Result, struct FLatentActionInfo LatentInfo, + const FScreenshotSize Size, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { if (!Slot) { - Result = ESaveGameResult::Failed; + Result = ESEContinueOrFail::Failed; return; } BPSaveSlot(Slot->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); @@ -519,17 +533,17 @@ inline bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded } inline void USaveManager::BPLoadSlotById( - int32 SlotId, ELoadGameResult& Result, struct FLatentActionInfo LatentInfo) + int32 SlotId, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) { BPLoadSlot(GetFileNameFromId(SlotId), Result, MoveTemp(LatentInfo)); } inline void USaveManager::BPLoadSlotByInfo( - const USaveSlot* Slot, ELoadGameResult& Result, FLatentActionInfo LatentInfo) + const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { if (!Slot) { - Result = ELoadGameResult::Failed; + Result = ESEContinueOrFail::Failed; return; } BPLoadSlot(Slot->FileName, Result, MoveTemp(LatentInfo)); From 58c5e29ac257c204a2493123ebd06a4c675ec20f Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 21 Sep 2023 14:43:14 +0200 Subject: [PATCH 4/7] Small changes to defautl settings and names --- Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp | 2 +- Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp | 2 +- Source/SaveExtension/Public/SaveSlot.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp index d5d235e..3c29130 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlot.cpp @@ -10,7 +10,7 @@ FText FAssetTypeAction_SaveSlot::GetName() const { - return LOCTEXT("FAssetTypeAction_SaveSlotName", "Save Info"); + return LOCTEXT("FAssetTypeAction_SaveSlotName", "Save Slot"); } FColor FAssetTypeAction_SaveSlot::GetTypeColor() const diff --git a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp index 828458d..d953edd 100644 --- a/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp +++ b/Source/Editor/Private/Asset/AssetTypeAction_SaveSlotData.cpp @@ -10,7 +10,7 @@ FText FAssetTypeAction_SaveSlotData::GetName() const { - return LOCTEXT("FAssetTypeAction_SaveSlotDataName", "Save Data"); + return LOCTEXT("FAssetTypeAction_SaveSlotDataName", "Save Slot Data"); } FColor FAssetTypeAction_SaveSlotData::GetTypeColor() const diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 4dd2812..af2a644 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -72,7 +72,7 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame /** If checked, will attempt to Save Game to first Slot found, timed event. */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") - bool bPeriodicSave = true; + bool bPeriodicSave = false; /** Interval in seconds for auto saving */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings", @@ -85,7 +85,7 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame /** If checked, will attempt to Load Game from last Slot found, when game starts */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") - bool bLoadOnStart = true; + bool bLoadOnStart = false; /** If true save files will be compressed * Performance: Can add from 10ms to 20ms to loading and saving (estimate) but reduce file sizes making From 98abc63ffa084e8882ad8fb27355c91a664e2cd8 Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 29 Sep 2023 00:14:43 +0200 Subject: [PATCH 5/7] Performance improvements & tasks refactor --- SaveExtension.uplugin | 19 +- .../SEActorClassFilterCustomization.cpp | 17 - .../SEActorClassFilterCustomization.h | 25 -- .../SEClassFilterCustomization.cpp | 17 +- .../SEClassFilterCustomization.h | 6 - .../SEComponentClassFilterCustomization.cpp | 17 - .../SEComponentClassFilterCustomization.h | 25 -- .../Customizations/SaveSlotDetails.cpp | 4 +- Source/Editor/Private/SaveExtensionEditor.cpp | 12 +- Source/SaveExtension/Private/LevelFilter.cpp | 28 +- .../Private/LifetimeComponent.cpp | 4 +- Source/SaveExtension/Private/SaveManager.cpp | 74 ++-- Source/SaveExtension/Private/SaveSlot.cpp | 50 ++- Source/SaveExtension/Private/SaveSlotData.cpp | 6 +- .../Private/Serialization/LevelRecords.cpp | 7 - .../Serialization/MTTask_SerializeActors.cpp | 38 +- .../Private/Serialization/SEDataTask.cpp | 52 +++ ...ataTask_Loader.cpp => SEDataTask_Load.cpp} | 334 ++++++++---------- .../Serialization/SEDataTask_LoadLevel.cpp | 72 ++++ ...DataTask_Saver.cpp => SEDataTask_Save.cpp} | 77 ++-- ...evelSaver.cpp => SEDataTask_SaveLevel.cpp} | 6 +- .../Private/Serialization/SlotDataTask.cpp | 91 ----- .../SlotDataTask_LevelLoader.cpp | 64 ---- Source/SaveExtension/Public/LevelFilter.h | 18 +- .../SaveExtension/Public/Misc/ClassFilter.h | 51 +-- .../Public/Multithreading/LoadFileTask.h | 12 +- Source/SaveExtension/Public/SaveManager.h | 27 +- Source/SaveExtension/Public/SaveSlot.h | 51 +-- Source/SaveExtension/Public/SaveSlotData.h | 3 +- .../Public/Serialization/LevelRecords.h | 9 +- .../Public/Serialization/MTTask.h | 17 +- .../Serialization/MTTask_SerializeActors.h | 28 +- .../Public/Serialization/Records.h | 6 +- .../{SlotDataTask.h => SEDataTask.h} | 57 ++- ...lotDataTask_Loader.h => SEDataTask_Load.h} | 43 +-- ...k_LevelLoader.h => SEDataTask_LoadLevel.h} | 20 +- ...SlotDataTask_Saver.h => SEDataTask_Save.h} | 37 +- ...sk_LevelSaver.h => SEDataTask_SaveLevel.h} | 20 +- Source/Test/Private/Files.spec.cpp | 8 +- Source/Test/Private/GameInstanceSpec.h | 4 +- Source/Test/Private/SavingSpec.h | 6 +- 41 files changed, 581 insertions(+), 881 deletions(-) delete mode 100644 Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp delete mode 100644 Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h delete mode 100644 Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp delete mode 100644 Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h create mode 100644 Source/SaveExtension/Private/Serialization/SEDataTask.cpp rename Source/SaveExtension/Private/Serialization/{SlotDataTask_Loader.cpp => SEDataTask_Load.cpp} (55%) create mode 100644 Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp rename Source/SaveExtension/Private/Serialization/{SlotDataTask_Saver.cpp => SEDataTask_Save.cpp} (78%) rename Source/SaveExtension/Private/Serialization/{SlotDataTask_LevelSaver.cpp => SEDataTask_SaveLevel.cpp} (79%) delete mode 100644 Source/SaveExtension/Private/Serialization/SlotDataTask.cpp delete mode 100644 Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp rename Source/SaveExtension/Public/Serialization/{SlotDataTask.h => SEDataTask.h} (66%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_Loader.h => SEDataTask_Load.h} (73%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_LevelLoader.h => SEDataTask_LoadLevel.h} (52%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_Saver.h => SEDataTask_Save.h} (77%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_LevelSaver.h => SEDataTask_SaveLevel.h} (55%) diff --git a/SaveExtension.uplugin b/SaveExtension.uplugin index 40c8e8b..3db46d6 100644 --- a/SaveExtension.uplugin +++ b/SaveExtension.uplugin @@ -21,13 +21,10 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", - "Linux", + "Mac", + "IOS", "Android", - "PS5", - "XboxOne", - "Switch", - "Mac" + "Linux" ] }, { @@ -36,13 +33,10 @@ "LoadingPhase": "PostEngineInit", "WhitelistPlatforms": [ "Win64", - "Win32", - "Linux", + "Mac", + "IOS", "Android", - "PS5", - "XboxOne", - "Switch", - "Mac" + "Linux" ] }, { @@ -51,7 +45,6 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", "Linux", "Mac" ] diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp deleted file mode 100644 index ac3f992..0000000 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Customizations/SEActorClassFilterCustomization.h" - -#include -#include - -#define LOCTEXT_NAMESPACE "FSEActorClassFilterCustomization" - - -TSharedPtr FSEActorClassFilterCustomization::GetFilterHandle( - TSharedRef StructPropertyHandle) -{ - return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEActorClassFilter, ClassFilter)); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h deleted file mode 100644 index 9078c45..0000000 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. -#pragma once - -#include "SEClassFilterCustomization.h" - - -class IPropertyHandle; - -class FSEActorClassFilterCustomization : public FSEClassFilterCustomization -{ -public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ - static TSharedRef MakeInstance() - { - return MakeShared(); - } - -protected: - virtual TSharedPtr GetFilterHandle( - TSharedRef StructPropertyHandle) override; -}; diff --git a/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp index 4f19d84..b9d0980 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp @@ -25,7 +25,6 @@ void FSEClassFilterCustomization::CustomizeHeader(TSharedRef RawStructData; - FilterHandle->AccessRawData(RawStructData); + StructHandle->AccessRawData(RawStructData); TArray Outers; - FilterHandle->GetOuterObjects(Outers); + StructHandle->GetOuterObjects(Outers); UObject* FirstOuter = Outers.Num() ? Outers[0] : nullptr; for (int32 ContainerIdx = 0; ContainerIdx < RawStructData.Num(); ++ContainerIdx) @@ -98,19 +97,19 @@ void FSEClassFilterCustomization::BuildEditableFilterList() TSharedRef FSEClassFilterCustomization::GetListContent() { - if (!FilterHandle.IsValid() || FilterHandle->GetProperty() == nullptr) + if (!StructHandle.IsValid() || StructHandle->GetProperty() == nullptr) { return SNullWidget::NullWidget; } - bool bReadOnly = FilterHandle->IsEditConst(); + bool bReadOnly = StructHandle->IsEditConst(); // clang-format off TSharedRef EditPopup = SNew(SClassFilter, EditableFilters) .ReadOnly(bReadOnly) .OnFilterChanged(this, &FSEClassFilterCustomization::RefreshClassList) - .PropertyHandle(FilterHandle); + .PropertyHandle(StructHandle); LastFilterPopup = EditPopup; return SNew(SVerticalBox) + SVerticalBox::Slot() @@ -137,12 +136,12 @@ void FSEClassFilterCustomization::OnPopupStateChanged(bool bIsOpened) void FSEClassFilterCustomization::OnClearClicked() { FScopedTransaction Transaction(LOCTEXT("ClassFilter_Filter", "Clear Filter")); - FilterHandle->NotifyPreChange(); + StructHandle->NotifyPreChange(); for (auto& Filter : EditableFilters) { *Filter.Filter = {}; } - FilterHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + StructHandle->NotifyPostChange(EPropertyChangeType::ValueSet); RefreshClassList(); } diff --git a/Source/Editor/Private/Customizations/SEClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEClassFilterCustomization.h index ed46b6c..fb9f20d 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterCustomization.h +++ b/Source/Editor/Private/Customizations/SEClassFilterCustomization.h @@ -26,7 +26,6 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE TArray> PreviewClasses; TSharedPtr StructHandle; - TSharedPtr FilterHandle; TSharedPtr EditButton; @@ -61,11 +60,6 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE virtual void PostRedo(bool bSuccess) override; //~ End FEditorUndoClient Interface - virtual TSharedPtr GetFilterHandle(TSharedRef StructPropertyHandle) - { - return StructHandle; - } - /** Build List of Editable Containers */ void BuildEditableFilterList(); diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp deleted file mode 100644 index c0e79af..0000000 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Customizations/SEComponentClassFilterCustomization.h" - -#include "PropertyHandle.h" - - -#define LOCTEXT_NAMESPACE "FSEComponentClassFilterCustomization" - - -TSharedPtr FSEComponentClassFilterCustomization::GetFilterHandle( - TSharedRef StructPropertyHandle) -{ - return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEComponentClassFilter, ClassFilter)); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h deleted file mode 100644 index 16a632f..0000000 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. -#pragma once - -#include "SEClassFilterCustomization.h" - - -class IPropertyHandle; - -class FSEComponentClassFilterCustomization : public FSEClassFilterCustomization -{ -public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ - static TSharedRef MakeInstance() - { - return MakeShared(); - } - -protected: - virtual TSharedPtr GetFilterHandle( - TSharedRef StructPropertyHandle) override; -}; diff --git a/Source/Editor/Private/Customizations/SaveSlotDetails.cpp b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp index 9ed50fb..7133195 100644 --- a/Source/Editor/Private/Customizations/SaveSlotDetails.cpp +++ b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp @@ -71,7 +71,7 @@ EVisibility FSaveSlotDetails::GetWarningVisibility() const { if (Slot.IsValid()) { - return Slot->GetFrameSplitSerialization() == ESaveASyncMode::OnlySync ? EVisibility::Collapsed + return Slot->GetFrameSplitSerialization() == ESEAsyncMode::SaveAndLoadSync ? EVisibility::Collapsed : EVisibility::Visible; } return EVisibility::Collapsed; @@ -81,7 +81,7 @@ bool FSaveSlotDetails::CanEditAsynchronous() const { if (Slot.IsValid()) { - return Slot->GetFrameSplitSerialization() != ESaveASyncMode::OnlySync; + return Slot->GetFrameSplitSerialization() != ESEAsyncMode::SaveAndLoadSync; } return true; } diff --git a/Source/Editor/Private/SaveExtensionEditor.cpp b/Source/Editor/Private/SaveExtensionEditor.cpp index cf68f32..9f648e0 100644 --- a/Source/Editor/Private/SaveExtensionEditor.cpp +++ b/Source/Editor/Private/SaveExtensionEditor.cpp @@ -4,10 +4,8 @@ #include "Asset/AssetTypeAction_SaveSlot.h" #include "Asset/AssetTypeAction_SaveSlotData.h" -#include "Customizations/SEActorClassFilterCustomization.h" #include "Customizations/SEClassFilterCustomization.h" #include "Customizations/SEClassFilterGraphPanelPinFactory.h" -#include "Customizations/SEComponentClassFilterCustomization.h" #include "Customizations/SaveSlotDetails.h" #include "Kismet2/KismetEditorUtilities.h" @@ -48,12 +46,10 @@ void FSaveExtensionEditor::RegisterPropertyTypeCustomizations() RegisterCustomPropertyTypeLayout("SEClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout( - "SEActorClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( - &FSEActorClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout( - "SEComponentClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( - &FSEComponentClassFilterCustomization::MakeInstance)); + RegisterCustomPropertyTypeLayout("SEActorClassFilter", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); + RegisterCustomPropertyTypeLayout("SEComponentClassFilter", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); RegisterCustomPinFactory(); } diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index e7247c5..53c5f85 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -13,42 +13,26 @@ const FName FSELevelFilter::TagNoPhysics{"!SavePhysics"}; const FName FSELevelFilter::TagNoTags{"!SaveTags"}; const FName FSELevelFilter::TagTransform{"SaveTransform"}; -void FSELevelFilter::FromSlot(const USaveSlot& Slot) -{ - ActorFilter = Slot.GetActorFilter(true); - LoadActorFilter = Slot.GetActorFilter(false); - bStoreComponents = Slot.bStoreComponents; - ComponentFilter = Slot.GetComponentFilter(true); - LoadComponentFilter = Slot.GetComponentFilter(false); -} void FSELevelFilter::BakeAllowedClasses() const { TRACE_CPUPROFILER_EVENT_SCOPE(FSELevelFilter::BakeAllowedClasses); ActorFilter.BakeAllowedClasses(); ComponentFilter.BakeAllowedClasses(); - LoadActorFilter.BakeAllowedClasses(); - LoadComponentFilter.BakeAllowedClasses(); -} - -bool FSELevelFilter::ShouldSave(const AActor* Actor) const -{ - return ActorFilter.IsClassAllowed(Actor->GetClass()); } -bool FSELevelFilter::ShouldSave(const UActorComponent* Component) const +bool FSELevelFilter::Stores(const AActor* Actor) const { - return IsValid(Component) && ComponentFilter.IsClassAllowed(Component->GetClass()); + return ActorFilter.IsAllowed(Actor->GetClass()); } -bool FSELevelFilter::ShouldLoad(const AActor* Actor) const +bool FSELevelFilter::StoresAnyComponents() const { - return LoadActorFilter.IsClassAllowed(Actor->GetClass()); + return ComponentFilter.IsAnyAllowed(); } - -bool FSELevelFilter::ShouldLoad(const UActorComponent* Component) const +bool FSELevelFilter::Stores(const UActorComponent* Component) const { - return IsValid(Component) && LoadComponentFilter.IsClassAllowed(Component->GetClass()); + return ComponentFilter.IsAllowed(Component->GetClass()); } bool FSELevelFilter::StoresTransform(const UActorComponent* Component) diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index 74c1ffd..a710974 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -49,7 +49,7 @@ void ULifetimeComponent::EndPlay(EEndPlayReason::Type Reason) void ULifetimeComponent::OnSaveBegan(const FSELevelFilter& Filter) { - if (Filter.ShouldSave(GetOwner())) + if (Filter.Stores(GetOwner())) { Saved.Broadcast(); } @@ -57,7 +57,7 @@ void ULifetimeComponent::OnSaveBegan(const FSELevelFilter& Filter) void ULifetimeComponent::OnLoadFinished(const FSELevelFilter& Filter, bool bError) { - if (Filter.ShouldSave(GetOwner())) + if (Filter.Stores(GetOwner())) { Resume.Broadcast(); } diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 1acd6d0..62bb91e 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -6,10 +6,10 @@ #include "Multithreading/LoadSlotsTask.h" #include "SaveFileHelpers.h" #include "SaveSettings.h" -#include "Serialization/SlotDataTask_LevelLoader.h" -#include "Serialization/SlotDataTask_LevelSaver.h" -#include "Serialization/SlotDataTask_Loader.h" -#include "Serialization/SlotDataTask_Saver.h" +#include "Serialization/SEDataTask_LoadLevel.h" +#include "Serialization/SEDataTask_SaveLevel.h" +#include "Serialization/SEDataTask_Load.h" +#include "Serialization/SEDataTask_Save.h" #include #include @@ -229,12 +229,12 @@ bool USaveManager::SaveSlot(FName SlotName, bool bOverrideIfNeeded, bool bScreen check(World); // Launch task, always fail if it didn't finish or wasn't scheduled - auto* Task = CreateTask() - ->Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) - ->Bind(OnSaved) - ->Start(); + auto& Task = CreateTask() + .Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) + .Bind(OnSaved) + .Start(); - return Task->IsSucceeded() || Task->IsScheduled(); + return Task.IsSucceeded() || Task.IsScheduled(); } bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) @@ -246,9 +246,8 @@ bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) AssureActiveSlot(); - auto* Task = CreateTask()->Setup(SlotName)->Bind(OnLoaded)->Start(); - - return Task->IsSucceeded() || Task->IsScheduled(); + auto& Task = CreateTask().Setup(SlotName).Bind(OnLoaded).Start(); + return Task.IsSucceeded() || Task.IsScheduled(); } bool USaveManager::DeleteSlot(FName SlotName) @@ -422,13 +421,13 @@ void USaveManager::SerializeStreamingLevel(ULevelStreaming* LevelStreaming) { if (!LevelStreaming->GetLoadedLevel()->bIsBeingRemoved) { - CreateTask()->Setup(LevelStreaming)->Start(); + CreateTask().Setup(LevelStreaming).Start(); } } void USaveManager::DeserializeStreamingLevel(ULevelStreaming* LevelStreaming) { - CreateTask()->Setup(LevelStreaming)->Start(); + CreateTask().Setup(LevelStreaming).Start(); } USaveSlot* USaveManager::LoadInfo(FName SlotName) @@ -450,17 +449,9 @@ USaveSlot* USaveManager::LoadInfo(FName SlotName) return Infos.Num() > 0 ? Infos[0] : nullptr; } -USaveSlotDataTask* USaveManager::CreateTask(TSubclassOf TaskType) -{ - USaveSlotDataTask* Task = NewObject(this, TaskType.Get()); - Task->Prepare(ActiveSlot); - Tasks.Add(Task); - return Task; -} - -void USaveManager::FinishTask(USaveSlotDataTask* Task) +void USaveManager::FinishTask(FSEDataTask* Task) { - Tasks.Remove(Task); + Tasks.RemoveAll([Task](auto& TaskPtr) { return TaskPtr.Get() == Task; }); // Start next task if (Tasks.Num() > 0) @@ -483,15 +474,14 @@ FName USaveManager::GetFileNameFromId(const int32 SlotId) const bool USaveManager::IsLoading() const { - return HasTasks() && - (Tasks[0]->IsA() || Tasks[0]->IsA()); + return HasTasks() && Tasks[0]->Type == ESETaskType::Load; } void USaveManager::Tick(float DeltaTime) { if (Tasks.Num()) { - USaveSlotDataTask* Task = Tasks[0]; + FSEDataTask* Task = Tasks[0].Get(); check(Task); if (Task->IsRunning()) { @@ -512,11 +502,12 @@ void USaveManager::UnsubscribeFromEvents(const TScriptInterfacetemplate Implements()); // C++ event @@ -525,14 +516,15 @@ void USaveManager::OnSaveBegan(const FSELevelFilter& Filter) Interface->OnSaveBegan(Filter); } ISaveExtensionInterface::Execute_ReceiveOnSaveBegan(Object, Filter); - }); + });*/ } -void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bError) +void USaveManager::OnSaveFinished(const bool bError) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnSaveFinished); - IterateSubscribedInterfaces([&Filter, bError](auto* Object) { + // TODO: Needs reworking + /*IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -541,7 +533,7 @@ void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bErro Interface->OnSaveFinished(Filter, bError); } ISaveExtensionInterface::Execute_ReceiveOnSaveFinished(Object, Filter, bError); - }); + });*/ if (!bError) { @@ -549,11 +541,11 @@ void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bErro } } -void USaveManager::OnLoadBegan(const FSELevelFilter& Filter) +void USaveManager::OnLoadBegan() { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadBegan); - IterateSubscribedInterfaces([&Filter](auto* Object) { + /*IterateSubscribedInterfaces([&Filter](auto* Object) { check(Object->template Implements()); // C++ event @@ -562,14 +554,14 @@ void USaveManager::OnLoadBegan(const FSELevelFilter& Filter) Interface->OnLoadBegan(Filter); } ISaveExtensionInterface::Execute_ReceiveOnLoadBegan(Object, Filter); - }); + });*/ } -void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bError) +void USaveManager::OnLoadFinished(const bool bError) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadFinished); - IterateSubscribedInterfaces([&Filter, bError](auto* Object) { + /*IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -578,7 +570,7 @@ void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bErro Interface->OnLoadFinished(Filter, bError); } ISaveExtensionInterface::Execute_ReceiveOnLoadFinished(Object, Filter, bError); - }); + });*/ if (!bError) { @@ -593,9 +585,9 @@ void USaveManager::OnMapLoadStarted(const FString& MapName) void USaveManager::OnMapLoadFinished(UWorld* LoadedWorld) { - if (auto* ActiveLoader = Cast(Tasks.Num() ? Tasks[0] : nullptr)) + if (IsLoading()) { - ActiveLoader->OnMapLoaded(); + static_cast(Tasks[0].Get())->OnMapLoaded(); } UpdateLevelStreamings(); diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 71bd96a..6f580c0 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -2,6 +2,8 @@ #include "SaveSlot.h" +#include "SaveFileHelpers.h" + #include #include #include @@ -126,29 +128,18 @@ int32 USaveSlot::GetIndex_Implementation() const return OnGetIndex(); } - -const FSEActorClassFilter& USaveSlot::GetActorFilter(bool bIsLoading) const -{ - return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; -} - -const FSEComponentClassFilter& USaveSlot::GetComponentFilter(bool bIsLoading) const -{ - return (bIsLoading && bUseLoadActorFilter) ? LoadComponentFilter : ComponentFilter; -} - bool USaveSlot::IsMTSerializationLoad() const { - return MultithreadedSerialization == ESaveASyncMode::LoadAsync || - MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedSerialization == ESEAsyncMode::LoadAsync || + MultithreadedSerialization == ESEAsyncMode::SaveAndLoadAsync; } bool USaveSlot::IsMTSerializationSave() const { - return MultithreadedSerialization == ESaveASyncMode::SaveAsync || - MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedSerialization == ESEAsyncMode::SaveAsync || + MultithreadedSerialization == ESEAsyncMode::SaveAndLoadAsync; } -ESaveASyncMode USaveSlot::GetFrameSplitSerialization() const +ESEAsyncMode USaveSlot::GetFrameSplitSerialization() const { return FrameSplittedSerialization; } @@ -159,29 +150,32 @@ float USaveSlot::GetMaxFrameMs() const bool USaveSlot::IsFrameSplitLoad() const { - return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESaveASyncMode::LoadAsync || - FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); + return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESEAsyncMode::LoadAsync || + FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsFrameSplitSave() const { - return !IsMTSerializationSave() && (FrameSplittedSerialization == ESaveASyncMode::SaveAsync || - FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); + return !IsMTSerializationSave() && (FrameSplittedSerialization == ESEAsyncMode::SaveAsync || + FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsMTFilesLoad() const { - return MultithreadedFiles == ESaveASyncMode::LoadAsync || - MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedFiles == ESEAsyncMode::LoadAsync || + MultithreadedFiles == ESEAsyncMode::SaveAndLoadAsync; } bool USaveSlot::IsMTFilesSave() const { - return MultithreadedFiles == ESaveASyncMode::SaveAsync || - MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedFiles == ESEAsyncMode::SaveAsync || + MultithreadedFiles == ESEAsyncMode::SaveAndLoadAsync; +} +void USaveSlot::GetLevelFilter_Implementation(bool bIsLoading, FSELevelFilter& OutFilter) const +{ + OnGetLevelFilter(bIsLoading, OutFilter); } -FSELevelFilter USaveSlot::ToFilter() const +void USaveSlot::OnGetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) const { - FSELevelFilter Filter{}; - Filter.FromSlot(*this); - return Filter; + OutFilter.ActorFilter = ActorFilter; + OutFilter.ComponentFilter = ComponentFilter; } diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index c3b7786..d030a15 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -15,9 +15,7 @@ void USaveSlotData::Serialize(FArchive& Ar) Ar << bStoreGameInstance; Ar << GameInstance; - static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; - LevelFilterType->SerializeItem(Ar, &GlobalLevelFilter, nullptr); - MainLevel.Serialize(Ar); + RootLevel.Serialize(Ar); Ar << SubLevels; } @@ -26,7 +24,7 @@ void USaveSlotData::CleanRecords(bool bKeepSublevels) // Clean Up serialization data GameInstance = {}; - MainLevel.CleanRecords(); + RootLevel.CleanRecords(); if (!bKeepSublevels) { SubLevels.Empty(); diff --git a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp index da85dfa..eb9ac3a 100644 --- a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp +++ b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp @@ -15,13 +15,6 @@ bool FLevelRecord::Serialize(FArchive& Ar) { Super::Serialize(Ar); - Ar << bOverrideGlobalFilter; - if (bOverrideGlobalFilter) - { - static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; - LevelFilterType->SerializeItem(Ar, &Filter, nullptr); - } - Ar << LevelScript; Ar << Actors; diff --git a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp index 16e4bac..f8a91f9 100644 --- a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp +++ b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp @@ -21,15 +21,21 @@ void FMTTask_SerializeActors::DoWork() SerializeGameInstance(); } + TArray ActorsToSerialize; for (int32 I = 0; I < Num; ++I) { const AActor* const Actor = (*LevelActors)[StartIndex + I]; - if (Actor && Filter.ShouldSave(Actor)) + if (Actor && Filter->Stores(Actor)) { - FActorRecord& Record = ActorRecords.AddDefaulted_GetRef(); - SerializeActor(Actor, Record); + ActorsToSerialize.Add(Actor); } } + + for (const AActor* Actor : ActorsToSerialize) + { + FActorRecord& Record = ActorRecords.AddDefaulted_GetRef(); + SerializeActor(Actor, Record); + } } void FMTTask_SerializeActors::SerializeGameInstance() @@ -56,9 +62,9 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& Record = {Actor}; Record.bHiddenInGame = Actor->IsHidden(); - Record.bIsProcedural = Filter.IsProcedural(Actor); + Record.bIsProcedural = Filter->IsProcedural(Actor); - if (Filter.StoresTags(Actor)) + if (Filter->StoresTags(Actor)) { Record.Tags = Actor->Tags; } @@ -67,18 +73,18 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& // Only save save-tags for (const auto& Tag : Actor->Tags) { - if (Filter.IsSaveTag(Tag)) + if (Filter->IsSaveTag(Tag)) { Record.Tags.Add(Tag); } } } - if (Filter.StoresTransform(Actor)) + if (Filter->StoresTransform(Actor)) { Record.Transform = Actor->GetTransform(); - if (Filter.StoresPhysics(Actor)) + if (Filter->StoresPhysics(Actor)) { USceneComponent* const Root = Actor->GetRootComponent(); if (Root && Root->Mobility == EComponentMobility::Movable) @@ -96,10 +102,7 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& } } - if (Filter.bStoreComponents) - { - SerializeActorComponents(Actor, Record, 1); - } + SerializeActorComponents(Actor, Record, 1); TRACE_CPUPROFILER_EVENT_SCOPE(Serialize); FMemoryWriter MemoryWriter(Record.Data, true); @@ -114,17 +117,22 @@ void FMTTask_SerializeActors::SerializeActorComponents( { TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents); + if (!Filter->StoresAnyComponents()) + { + return; + } + const TSet& Components = Actor->GetComponents(); for (auto* Component : Components) { TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents | Component); - if (Filter.ShouldSave(Component)) + if (IsValid(Component) && Filter->Stores(Component)) { FComponentRecord ComponentRecord; ComponentRecord.Name = Component->GetFName(); ComponentRecord.Class = Component->GetClass(); - if (Filter.StoresTransform(Component)) + if (Filter->StoresTransform(Component)) { const USceneComponent* Scene = CastChecked(Component); if (Scene->Mobility == EComponentMobility::Movable) @@ -133,7 +141,7 @@ void FMTTask_SerializeActors::SerializeActorComponents( } } - if (Filter.StoresTags(Component)) + if (Filter->StoresTags(Component)) { ComponentRecord.Tags = Component->ComponentTags; } diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp new file mode 100644 index 0000000..fe47f1a --- /dev/null +++ b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp @@ -0,0 +1,52 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Serialization/SEDataTask.h" + +#include "SaveManager.h" + + +///////////////////////////////////////////////////// +// USaveDataTask + +FSEDataTask& FSEDataTask::Start() +{ + // If not running and first task is this + if (!bRunning && Manager->Tasks.Num() > 0 && Manager->Tasks[0].Get() == this) + { + bRunning = true; + OnStart(); + } + return *this; +} + +void FSEDataTask::Finish(bool bSuccess) +{ + if (bRunning) + { + OnFinish(bSuccess); + Manager->FinishTask(this); + bFinished = true; + bSucceeded = bSuccess; + } +} + +bool FSEDataTask::IsScheduled() const +{ + return Manager->Tasks.ContainsByPredicate([this](auto& Task) { + return Task.Get() == this; + }); +} + +FLevelRecord* FSEDataTask::FindLevelRecord(const ULevelStreaming* Level) const +{ + if (Level) + { + return SlotData->SubLevels.FindByKey(Level); + } + return &SlotData->RootLevel; +} + +UWorld* FSEDataTask::GetWorld() const +{ + return Manager->GetWorld(); +} diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp similarity index 55% rename from Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp rename to Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 6cde5f2..293b194 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/SlotDataTask_Loader.h" +#include "Serialization/SEDataTask_Load.h" #include "Misc/SlotHelpers.h" #include "SaveManager.h" @@ -14,38 +14,20 @@ ///////////////////////////////////////////////////// -// Helpers +// USaveDataTask_Loader -namespace Loader +FSEDataTask_Load::~FSEDataTask_Load() { - static int32 RemoveSingleRecordPtrSwap( - TArray& Records, AActor* Actor, bool bAllowShrinking = true) + if (LoadDataTask) { - if (!Actor) - { - return 0; - } - - const int32 I = Records.IndexOfByPredicate([Records, Actor](auto* Record) { - return *Record == Actor; - }); - if (I != INDEX_NONE) - { - Records.RemoveAtSwap(I, 1, bAllowShrinking); - return 1; - } - return 0; + LoadDataTask->EnsureCompletion(false); + delete LoadDataTask; } -} // namespace Loader - - -///////////////////////////////////////////////////// -// USaveDataTask_Loader +} -void USaveSlotDataTask_Loader::OnStart() +void FSEDataTask_Load::OnStart() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnStart); - USaveManager* Manager = GetManager(); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::OnStart); Slot = Manager->LoadInfo(SlotName); SELog(Slot, "Loading from Slot " + SlotName.ToString()); @@ -78,7 +60,7 @@ void USaveSlotDataTask_Loader::OnStart() return; } - UGameplayStatics::OpenLevel(this, FName{MapToOpen}); + UGameplayStatics::OpenLevel(Manager, FName{MapToOpen}); SELog(Slot, "Slot '" + SlotName.ToString() + "' is recorded on another Map. Loading before charging slot.", @@ -95,9 +77,9 @@ void USaveSlotDataTask_Loader::OnStart() } } -void USaveSlotDataTask_Loader::Tick(float DeltaTime) +void FSEDataTask_Load::Tick(float DeltaTime) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::Tick); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::Tick); switch (LoadState) { case ELoadDataTaskState::Deserializing: @@ -115,9 +97,9 @@ void USaveSlotDataTask_Loader::Tick(float DeltaTime) } } -void USaveSlotDataTask_Loader::OnFinish(bool bSuccess) +void FSEDataTask_Load::OnFinish(bool bSuccess) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnFinish); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::OnFinish); if (bSuccess) { SELog(Slot, "Finished Loading", FColor::Green); @@ -126,21 +108,10 @@ void USaveSlotDataTask_Loader::OnFinish(bool bSuccess) // Execute delegates Delegate.ExecuteIfBound((bSuccess) ? Slot : nullptr); - GetManager()->OnLoadFinished(SlotData ? GetGlobalFilter() : FSELevelFilter{}, !bSuccess); -} - -void USaveSlotDataTask_Loader::BeginDestroy() -{ - if (LoadDataTask) - { - LoadDataTask->EnsureCompletion(false); - delete LoadDataTask; - } - - Super::BeginDestroy(); + Manager->OnLoadFinished(!bSuccess); } -void USaveSlotDataTask_Loader::OnMapLoaded() +void FSEDataTask_Load::OnMapLoaded() { if (LoadState != ELoadDataTaskState::LoadingMap) { @@ -167,9 +138,9 @@ void USaveSlotDataTask_Loader::OnMapLoaded() } } -void USaveSlotDataTask_Loader::StartDeserialization() +void FSEDataTask_Load::StartDeserialization() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::StartDeserialization); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::StartDeserialization); check(Slot); LoadState = ELoadDataTaskState::Deserializing; @@ -184,11 +155,10 @@ void USaveSlotDataTask_Loader::StartDeserialization() Slot->Stats.LoadDate = FDateTime::Now(); - GetManager()->OnLoadBegan(GetGlobalFilter()); // Apply current Info if succeeded - GetManager()->AssignActiveSlot(Slot); + Manager->AssignActiveSlot(Slot); - BakeAllFilters(); + Manager->OnLoadBegan(); BeforeDeserialize(); @@ -198,9 +168,9 @@ void USaveSlotDataTask_Loader::StartDeserialization() DeserializeSync(); } -void USaveSlotDataTask_Loader::StartLoadingData() +void FSEDataTask_Load::StartLoadingData() { - LoadDataTask = new FAsyncTask(GetManager(), SlotName.ToString()); + LoadDataTask = new FAsyncTask(Manager, SlotName.ToString()); if (Slot->IsMTFilesLoad()) LoadDataTask->StartBackgroundTask(); @@ -208,7 +178,7 @@ void USaveSlotDataTask_Loader::StartLoadingData() LoadDataTask->StartSynchronousTask(); } -USaveSlotData* USaveSlotDataTask_Loader::GetLoadedData() const +USaveSlotData* FSEDataTask_Load::GetLoadedData() const { if (IsDataLoaded()) { @@ -217,9 +187,9 @@ USaveSlotData* USaveSlotDataTask_Loader::GetLoadedData() const return nullptr; } -void USaveSlotDataTask_Loader::BeforeDeserialize() +void FSEDataTask_Load::BeforeDeserialize() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::BeforeDeserialize); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::BeforeDeserialize); UWorld* World = GetWorld(); // Set current game time to the saved value @@ -231,9 +201,9 @@ void USaveSlotDataTask_Loader::BeforeDeserialize() } } -void USaveSlotDataTask_Loader::DeserializeSync() +void FSEDataTask_Load::DeserializeSync() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeSync); const UWorld* World = GetWorld(); check(World); @@ -259,34 +229,31 @@ void USaveSlotDataTask_Loader::DeserializeSync() FinishedDeserializing(); } -void USaveSlotDataTask_Loader::DeserializeLevelSync( +void FSEDataTask_Load::DeserializeLevelSync( const ULevel* Level, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeLevelSync); if (!IsValid(Level)) + { return; + } const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); - if (FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel)) + const FLevelRecord& LevelRecord = *FindLevelRecord(StreamingLevel); + for(const auto& RecordToActor : LevelRecord.RecordsToActors) { - const auto& Filter = GetLevelFilter(*LevelRecord); - - for (auto ActorItr = Level->Actors.CreateConstIterator(); ActorItr; ++ActorItr) - { - auto* Actor = *ActorItr; - if (IsValid(Actor) && Filter.ShouldSave(Actor)) - { - DeserializeLevel_Actor(Actor, *LevelRecord, Filter); - } - } + const FActorRecord* Record = RecordToActor.Key; + AActor* Actor = RecordToActor.Value.Get(); + check(Record && Actor); + DeserializeActor(Actor, *Record, LevelRecord); } } -void USaveSlotDataTask_Loader::DeserializeASync() +void FSEDataTask_Load::DeserializeASync() { // Deserialize world { @@ -297,7 +264,7 @@ void USaveSlotDataTask_Loader::DeserializeASync() } } -void USaveSlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStreaming* StreamingLevel) +void FSEDataTask_Load::DeserializeLevelASync(ULevel* Level, ULevelStreaming* StreamingLevel) { check(IsValid(Level)); @@ -318,48 +285,37 @@ void USaveSlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStream CurrentSLevel = StreamingLevel; CurrentActorIndex = 0; - // Copy actors array. New actors won't be considered for deserialization - CurrentLevelActors.Empty(Level->Actors.Num()); - for (auto* Actor : Level->Actors) - { - if (IsValid(Actor)) - { - CurrentLevelActors.Add(Actor); - } - } - DeserializeASyncLoop(StartMS); } -void USaveSlotDataTask_Loader::DeserializeASyncLoop(float StartMS) +void FSEDataTask_Load::DeserializeASyncLoop(float StartMS) { - FLevelRecord* LevelRecord = FindLevelRecord(CurrentSLevel.Get()); - if (!LevelRecord) - { - return; - } - - const auto& Filter = GetLevelFilter(*LevelRecord); - if (StartMS <= 0) { StartMS = GetTimeMilliseconds(); } + FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); + // Continue Iterating actors every tick - for (; CurrentActorIndex < CurrentLevelActors.Num(); ++CurrentActorIndex) + for (; CurrentActorIndex < LevelRecord.RecordsToActors.Num(); ++CurrentActorIndex) { - AActor* const Actor{CurrentLevelActors[CurrentActorIndex].Get()}; - if (IsValid(Actor) && Filter.ShouldSave(Actor)) + auto& RecordToActor = LevelRecord.RecordsToActors[CurrentActorIndex]; + + const FActorRecord* Record = RecordToActor.Key; + AActor* Actor = RecordToActor.Value.Get(); + check(Record); + if (!Actor) { - DeserializeLevel_Actor(Actor, *LevelRecord, Filter); + continue; + } + DeserializeActor(Actor, *Record, LevelRecord); - const float CurrentMS = GetTimeMilliseconds(); + const float CurrentMS = GetTimeMilliseconds(); + if (CurrentMS - StartMS >= MaxFrameMs) + { // If x milliseconds passed, stop and continue on next frame - if (CurrentMS - StartMS >= MaxFrameMs) - { - return; - } + return; } } @@ -380,49 +336,66 @@ void USaveSlotDataTask_Loader::DeserializeASyncLoop(float StartMS) FinishedDeserializing(); } -void USaveSlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) +void FSEDataTask_Load::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::PrepareLevel); - - const auto& Filter = GetLevelFilter(LevelRecord); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::PrepareLevel); + Slot->GetLevelFilter(true, LevelRecord.Filter); + LevelRecord.Filter.BakeAllowedClasses(); // Records not contained in Scene Actors => Actors to be Respawned // Scene Actors not contained in loaded records => Actors to be Destroyed // The rest => Just deserialize - TArray ActorsToSpawn; - ActorsToSpawn.Reserve(LevelRecord.Actors.Num()); + TArray ActorRecordsToSpawn; + ActorRecordsToSpawn.Reserve(LevelRecord.Actors.Num()); for (FActorRecord& Record : LevelRecord.Actors) { - ActorsToSpawn.Add(&Record); + ActorRecordsToSpawn.Add(&Record); } TArray ActorsToDestroy{}; - { - // O(M*Log(N)) + { // Filter actors by whether they should be destroyed or spawned - O(M*Log(N)) for (AActor* const Actor : Level->Actors) { - // Remove records which actors do exist - const bool bFoundActorRecord = Loader::RemoveSingleRecordPtrSwap(ActorsToSpawn, Actor, false) > 0; + if (UNLIKELY(!Actor)) + { + continue; + } - if (Actor && Filter.ShouldSave(Actor)) + const int32 Index = ActorRecordsToSpawn.IndexOfByPredicate([Actor](auto* Record) { + return *Record == Actor; + }); + if (Index != INDEX_NONE) // Actor found, therefore doesn't need to be spawned { - if (!bFoundActorRecord) // Don't destroy level actors + if (LevelRecord.Filter.Stores(Actor)) { - // If the actor wasn't found, mark it for destruction - Actor->Destroy(); + FActorRecord* Record = ActorRecordsToSpawn[Index]; + LevelRecord.RecordsToActors.Add({Record, Actor}); } + ActorRecordsToSpawn.RemoveAtSwap(Index, 1, false); + } + else if (LevelRecord.Filter.Stores(Actor)) + { + ActorsToDestroy.Add(Actor); } + // TODO: Consider unmatching class actors to be respawned } - ActorsToSpawn.Shrink(); + + } + + // The serializable actors that were not found will be destroyed + for (AActor* Actor : ActorsToDestroy) + { + Actor->Destroy(); } - // Create Actors that doesn't exist now but were saved - RespawnActors(ActorsToSpawn, Level); + // Spawn Actors that don't exist but were saved + ActorRecordsToSpawn.Shrink(); + RespawnActors(ActorRecordsToSpawn, Level, LevelRecord); } -void USaveSlotDataTask_Loader::FinishedDeserializing() +void FSEDataTask_Load::FinishedDeserializing() { // Clean serialization data SlotData->CleanRecords(true); @@ -430,15 +403,15 @@ void USaveSlotDataTask_Loader::FinishedDeserializing() Finish(true); } -void USaveSlotDataTask_Loader::PrepareAllLevels() +void FSEDataTask_Load::PrepareAllLevels() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::PrepareAllLevels); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::PrepareAllLevels); const UWorld* World = GetWorld(); check(World); // Prepare Main level - PrepareLevel(World->GetCurrentLevel(), SlotData->MainLevel); + PrepareLevel(World->GetCurrentLevel(), SlotData->RootLevel); // Prepare other loaded sub-levels const TArray& Levels = World->GetStreamingLevels(); @@ -455,9 +428,9 @@ void USaveSlotDataTask_Loader::PrepareAllLevels() } } -void USaveSlotDataTask_Loader::RespawnActors(const TArray& Records, const ULevel* Level) +void FSEDataTask_Load::RespawnActors(const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::RespawnActors); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::RespawnActors); FActorSpawnParameters SpawnInfo{}; SpawnInfo.OverrideLevel = const_cast(Level); @@ -473,23 +446,11 @@ void USaveSlotDataTask_Loader::RespawnActors(const TArray& Record // We update the name on the record in case it changed Record->Name = NewActor->GetFName(); + LevelRecord.RecordsToActors.Add({Record, NewActor}); } } -void USaveSlotDataTask_Loader::DeserializeLevel_Actor( - AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter) -{ - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeLevel_Actor); - - // Find the record - const FActorRecord* const Record = LevelRecord.Actors.FindByKey(Actor); - if (Record && Record->IsValid() && Record->Class == Actor->GetClass()) - { - DeserializeActor(Actor, *Record, Filter); - } -} - -void USaveSlotDataTask_Loader::DeserializeGameInstance() +void FSEDataTask_Load::DeserializeGameInstance() { bool bSuccess = true; auto* GameInstance = GetWorld()->GetGameInstance(); @@ -509,41 +470,46 @@ void USaveSlotDataTask_Loader::DeserializeGameInstance() SELog(Slot, "Game Instance '" + Record.Name.ToString() + "'", FColor::Green, !bSuccess, 1); } -bool USaveSlotDataTask_Loader::DeserializeActor( - AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter) +bool FSEDataTask_Load::DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeActor); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeActor); + + if (Actor->GetClass() != ActorRecord.Class) + { + SELog(Slot, "Actor '" + ActorRecord.Name.ToString() + "' already exists but class doesn't match", FColor::Green, true, 1); + return false; + } // Always load saved tags - Actor->Tags = Record.Tags; + Actor->Tags = ActorRecord.Tags; const bool bSavesPhysics = FSELevelFilter::StoresPhysics(Actor); if (FSELevelFilter::StoresTransform(Actor)) { - Actor->SetActorTransform(Record.Transform); + Actor->SetActorTransform(ActorRecord.Transform); if (FSELevelFilter::StoresPhysics(Actor)) { USceneComponent* Root = Actor->GetRootComponent(); if (auto* Primitive = Cast(Root)) { - Primitive->SetPhysicsLinearVelocity(Record.LinearVelocity); - Primitive->SetPhysicsAngularVelocityInRadians(Record.AngularVelocity); + Primitive->SetPhysicsLinearVelocity(ActorRecord.LinearVelocity); + Primitive->SetPhysicsAngularVelocityInRadians(ActorRecord.AngularVelocity); } else { - Root->ComponentVelocity = Record.LinearVelocity; + Root->ComponentVelocity = ActorRecord.LinearVelocity; } } } - Actor->SetActorHiddenInGame(Record.bHiddenInGame); + Actor->SetActorHiddenInGame(ActorRecord.bHiddenInGame); - DeserializeActorComponents(Actor, Record, Filter, 2); + DeserializeActorComponents(Actor, ActorRecord, LevelRecord, 2); { // Serialize from Record Data - FMemoryReader MemoryReader(Record.Data, true); + FMemoryReader MemoryReader(ActorRecord.Data, true); FSEArchive Archive(MemoryReader, false); Actor->Serialize(Archive); } @@ -551,55 +517,55 @@ bool USaveSlotDataTask_Loader::DeserializeActor( return true; } -void USaveSlotDataTask_Loader::DeserializeActorComponents( - AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 Indent) +void FSEDataTask_Load::DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 Indent) { - if (Filter.bStoreComponents) + TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents); + + if (!LevelRecord.Filter.StoresAnyComponents()) { - TRACE_CPUPROFILER_EVENT_SCOPE(UUSaveSlotDataTask_Loader::DeserializeActorComponents); + return; + } - const TSet& Components = Actor->GetComponents(); - for (auto* Component : Components) + for (auto* Component : Actor->GetComponents()) + { + if (!IsValid(Component) || !LevelRecord.Filter.Stores(Component)) { - if (!Filter.ShouldSave(Component)) - { - continue; - } + continue; + } - // Find the record - const FComponentRecord* Record = ActorRecord.ComponentRecords.FindByKey(Component); - if (!Record) - { - SELog(Slot, "Component '" + Component->GetFName().ToString() + "' - Record not found", - FColor::Red, false, Indent + 1); - continue; - } + // Find the record + const FComponentRecord* Record = ActorRecord.ComponentRecords.FindByKey(Component); + if (!Record) + { + SELog(Slot, "Component '" + Component->GetFName().ToString() + "' - Record not found", + FColor::Red, false, Indent + 1); + continue; + } - if (FSELevelFilter::StoresTransform(Component)) + if (FSELevelFilter::StoresTransform(Component)) + { + USceneComponent* Scene = CastChecked(Component); + if (Scene->Mobility == EComponentMobility::Movable) { - USceneComponent* Scene = CastChecked(Component); - if (Scene->Mobility == EComponentMobility::Movable) - { - Scene->SetRelativeTransform(Record->Transform); - } + Scene->SetRelativeTransform(Record->Transform); } + } - if (FSELevelFilter::StoresTags(Component)) - { - Component->ComponentTags = Record->Tags; - } + if (FSELevelFilter::StoresTags(Component)) + { + Component->ComponentTags = Record->Tags; + } - if (!Component->GetClass()->IsChildOf()) - { - FMemoryReader MemoryReader(Record->Data, true); - FSEArchive Archive(MemoryReader, false); - Component->Serialize(Archive); - } + if (!Component->GetClass()->IsChildOf()) + { + FMemoryReader MemoryReader(Record->Data, true); + FSEArchive Archive(MemoryReader, false); + Component->Serialize(Archive); } } } -void USaveSlotDataTask_Loader::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const +void FSEDataTask_Load::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const { OutLevelStreaming = nullptr; diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp new file mode 100644 index 0000000..cfcd469 --- /dev/null +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp @@ -0,0 +1,72 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Serialization/SEDataTask_LoadLevel.h" + + +///////////////////////////////////////////////////// +// USaveDataTask_LevelLoader + +void FSEDataTask_LoadLevel::OnStart() +{ + if (!SlotData || !StreamingLevel || !StreamingLevel->IsLevelLoaded()) + { + Finish(false); + return; + } + + FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); + if (!LevelRecord) + { + Finish(false); + return; + } + + PrepareLevel(StreamingLevel->GetLoadedLevel(), *LevelRecord); + + if (Slot->IsFrameSplitLoad()) + { + DeserializeLevelASync(StreamingLevel->GetLoadedLevel(), StreamingLevel); + } + else + { + DeserializeLevelSync(StreamingLevel->GetLoadedLevel(), StreamingLevel); + FinishedDeserializing(); + return; + } +} + +void FSEDataTask_LoadLevel::DeserializeASyncLoop(float StartMS /*= 0.0f*/) +{ + + if (StartMS <= 0) + { + StartMS = GetTimeMilliseconds(); + } + + FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); + + // Continue Iterating actors every tick + for (; CurrentActorIndex < LevelRecord.RecordsToActors.Num(); ++CurrentActorIndex) + { + auto& RecordToActor = LevelRecord.RecordsToActors[CurrentActorIndex]; + + const FActorRecord* Record = RecordToActor.Key; + AActor* Actor = RecordToActor.Value.Get(); + check(Record); + if (!Actor) + { + continue; + } + DeserializeActor(Actor, *Record, LevelRecord); + + const float CurrentMS = GetTimeMilliseconds(); + if (CurrentMS - StartMS >= MaxFrameMs) + { + // If x milliseconds passed, stop and continue on next frame + return; + } + } + + // All levels deserialized + FinishedDeserializing(); +} diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp similarity index 78% rename from Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp rename to Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 971d1ab..27864e9 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/SlotDataTask_Saver.h" +#include "Serialization/SEDataTask_Save.h" #include "Misc/SlotHelpers.h" #include "SaveFileHelpers.h" @@ -13,12 +13,20 @@ ///////////////////////////////////////////////////// -// USaveDataTask_Saver +// FSEDataTask_Save -void USaveSlotDataTask_Saver::OnStart() +FSEDataTask_Save::~FSEDataTask_Save() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnStart); - USaveManager* Manager = GetManager(); + if (SaveTask) + { + SaveTask->EnsureCompletion(false); + delete SaveTask; + } +} + +void FSEDataTask_Save::OnStart() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::OnStart); Manager->AssureActiveSlot(); bool bSave = true; @@ -46,7 +54,7 @@ void USaveSlotDataTask_Saver::OnStart() { const UWorld* World = GetWorld(); - GetManager()->OnSaveBegan(GetGlobalFilter()); + Manager->OnSaveBegan(); Slot = Manager->GetActiveSlot(); SlotData = Slot->GetData(); @@ -92,7 +100,6 @@ void USaveSlotDataTask_Saver::OnStart() SlotData->Map = SlotData->Map; SlotData->bStoreGameInstance = Slot->bStoreGameInstance; - SlotData->GlobalLevelFilter = Slot->ToFilter(); SerializeWorld(); SaveFile(); @@ -101,10 +108,10 @@ void USaveSlotDataTask_Saver::OnStart() Finish(false); } -void USaveSlotDataTask_Saver::Tick(float DeltaTime) +void FSEDataTask_Save::Tick(float DeltaTime) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::Tick); - Super::Tick(DeltaTime); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::Tick); + FSEDataTask::Tick(DeltaTime); if (SaveTask && SaveTask->IsDone()) { @@ -122,9 +129,9 @@ void USaveSlotDataTask_Saver::Tick(float DeltaTime) } } -void USaveSlotDataTask_Saver::OnFinish(bool bSuccess) +void FSEDataTask_Save::OnFinish(bool bSuccess) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnFinish); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::OnFinish); if (bSuccess) { // Clean serialization data @@ -134,34 +141,22 @@ void USaveSlotDataTask_Saver::OnFinish(bool bSuccess) } // Execute delegates - USaveManager* Manager = GetManager(); - check(Manager); Delegate.ExecuteIfBound((Manager && bSuccess) ? Manager->GetActiveSlot() : nullptr); - Manager->OnSaveFinished(SlotData ? GetGlobalFilter() : FSELevelFilter{}, !bSuccess); -} -void USaveSlotDataTask_Saver::BeginDestroy() -{ - if (SaveTask) - { - SaveTask->EnsureCompletion(false); - delete SaveTask; - } - - Super::BeginDestroy(); + Manager->OnSaveFinished(!bSuccess); } -void USaveSlotDataTask_Saver::SerializeWorld() +void FSEDataTask_Save::SerializeWorld() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeWorld); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeWorld); // Must have Authority - if (!GetWorld()->GetAuthGameMode()) + const UWorld* World = GetWorld(); + if (!World->GetAuthGameMode()) { return; } - const UWorld* World = GetWorld(); SELog(Slot, "World '" + World->GetName() + "'", FColor::Green, false, 1); const TArray& Levels = World->GetStreamingLevels(); @@ -184,10 +179,8 @@ void USaveSlotDataTask_Saver::SerializeWorld() RunScheduledTasks(); } -void USaveSlotDataTask_Saver::PrepareAllLevels(const TArray& Levels) +void FSEDataTask_Save::PrepareAllLevels(const TArray& Levels) { - BakeAllFilters(); - // Create the sub-level records if non existent for (const ULevelStreaming* Level : Levels) { @@ -198,10 +191,10 @@ void USaveSlotDataTask_Saver::PrepareAllLevels(const TArray& L } } -void USaveSlotDataTask_Saver::SerializeLevelSync( +void FSEDataTask_Save::SerializeLevelSync( const ULevel* Level, int32 AssignedTasks, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeLevelSync); check(IsValid(Level)); if (!Slot->IsMTSerializationSave()) @@ -214,7 +207,7 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level - FLevelRecord* LevelRecord = &SlotData->MainLevel; + FLevelRecord* LevelRecord = &SlotData->RootLevel; if (StreamingLevel) { LevelRecord = FindLevelRecord(StreamingLevel); @@ -224,8 +217,6 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( // Empty level record before serializing it LevelRecord->CleanRecords(); - auto& Filter = GetLevelFilter(*LevelRecord); - const int32 MinObjectsPerTask = 40; const int32 ActorCount = Level->Actors.Num(); const int32 NumBalancedPerTask = FMath::CeilToInt((float) ActorCount / AssignedTasks); @@ -242,15 +233,15 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( bool bStoreGameInstance = Index <= 0 && SlotData->bStoreGameInstance; // Add new Task Tasks.Emplace(FMTTask_SerializeActors{GetWorld(), SlotData, &Level->Actors, Index, NumToSerialize, - bStoreGameInstance, LevelRecord, Filter}); + bStoreGameInstance, LevelRecord, &LevelRecord->Filter}); Index += NumToSerialize; } } -void USaveSlotDataTask_Saver::RunScheduledTasks() +void FSEDataTask_Save::RunScheduledTasks() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::RunScheduledTasks); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::RunScheduledTasks); // Start all serialization tasks if (Tasks.Num() > 0) { @@ -277,11 +268,9 @@ void USaveSlotDataTask_Saver::RunScheduledTasks() Tasks.Empty(); } -void USaveSlotDataTask_Saver::SaveFile() +void FSEDataTask_Save::SaveFile() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SaveFile); - USaveManager* Manager = GetManager(); - + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SaveFile); SaveTask = new FAsyncTask(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp similarity index 79% rename from Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp rename to Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp index cd1bda8..d265fe1 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp @@ -1,12 +1,12 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/SlotDataTask_LevelSaver.h" +#include "Serialization/SEDataTask_SaveLevel.h" ///////////////////////////////////////////////////// // FSaveDataTask_LevelSaver -void USaveSlotDataTask_LevelSaver::OnStart() +void FSEDataTask_SaveLevel::OnStart() { if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) { @@ -17,8 +17,6 @@ void USaveSlotDataTask_LevelSaver::OnStart() return; } - GetLevelFilter(*LevelRecord).BakeAllowedClasses(); - const int32 NumberOfThreads = FMath::Max(1, FPlatformMisc::NumberOfWorkerThreadsToSpawn()); SerializeLevelSync(StreamingLevel->GetLoadedLevel(), NumberOfThreads, StreamingLevel); diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp deleted file mode 100644 index b25eb4c..0000000 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Serialization/SlotDataTask.h" - -#include "SaveManager.h" - - -///////////////////////////////////////////////////// -// USaveDataTask - -USaveSlotDataTask* USaveSlotDataTask::Start() -{ - const USaveManager* Manager = GetManager(); - - // If not running and first task is this - if (!bRunning && Manager->Tasks.Num() > 0 && Manager->Tasks[0] == this) - { - bRunning = true; - OnStart(); - } - return this; -} - -void USaveSlotDataTask::Finish(bool bSuccess) -{ - if (bRunning) - { - OnFinish(bSuccess); - MarkAsGarbage(); - GetManager()->FinishTask(this); - bFinished = true; - bSucceeded = bSuccess; - } -} - -bool USaveSlotDataTask::IsScheduled() const -{ - return GetManager()->Tasks.Contains(this); -} - -USaveManager* USaveSlotDataTask::GetManager() const -{ - return Cast(GetOuter()); -} - -void USaveSlotDataTask::BakeAllFilters() -{ - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask::BakeAllFilters); - SlotData->GlobalLevelFilter.BakeAllowedClasses(); - - if (SlotData->MainLevel.bOverrideGlobalFilter) - { - SlotData->MainLevel.Filter.BakeAllowedClasses(); - } - - for (const auto& Level : SlotData->SubLevels) - { - if (Level.bOverrideGlobalFilter) - { - Level.Filter.BakeAllowedClasses(); - } - } -} - -const FSELevelFilter& USaveSlotDataTask::GetGlobalFilter() const -{ - check(SlotData); - return SlotData->GlobalLevelFilter; -} - -const FSELevelFilter& USaveSlotDataTask::GetLevelFilter(const FLevelRecord& Level) const -{ - if (Level.bOverrideGlobalFilter) - { - return Level.Filter; - } - return GetGlobalFilter(); -} - -FLevelRecord* USaveSlotDataTask::FindLevelRecord(const ULevelStreaming* Level) const -{ - if (!Level) - return &SlotData->MainLevel; - else // Find the Sub-Level - return SlotData->SubLevels.FindByKey(Level); -} - -UWorld* USaveSlotDataTask::GetWorld() const -{ - return GetOuter()->GetWorld(); -} diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp deleted file mode 100644 index 9e460ab..0000000 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Serialization/SlotDataTask_LevelLoader.h" - - -///////////////////////////////////////////////////// -// USaveDataTask_LevelLoader - -void USaveSlotDataTask_LevelLoader::OnStart() -{ - if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) - { - FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); - if (!LevelRecord) - { - Finish(false); - return; - } - - GetLevelFilter(*LevelRecord).BakeAllowedClasses(); - - if (Slot->IsFrameSplitLoad()) - { - DeserializeLevelASync(StreamingLevel->GetLoadedLevel(), StreamingLevel); - } - else - { - DeserializeLevelSync(StreamingLevel->GetLoadedLevel(), StreamingLevel); - FinishedDeserializing(); - } - return; - } - Finish(false); -} - -void USaveSlotDataTask_LevelLoader::DeserializeASyncLoop(float StartMS /*= 0.0f*/) -{ - FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); - - if (StartMS <= 0) - { - StartMS = GetTimeMilliseconds(); - } - - const auto& Filter = GetLevelFilter(LevelRecord); - - // Continue Iterating actors every tick - for (; CurrentActorIndex < CurrentLevelActors.Num(); ++CurrentActorIndex) - { - AActor* Actor{CurrentLevelActors[CurrentActorIndex].Get()}; - if (Actor) - { - DeserializeLevel_Actor(Actor, LevelRecord, Filter); - - const float CurrentMS = GetTimeMilliseconds(); - // If x milliseconds passed, continue on next frame - if (CurrentMS - StartMS >= MaxFrameMs) - return; - } - } - - // All levels deserialized - FinishedDeserializing(); -} diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 5a3641f..8748961 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -29,29 +29,17 @@ struct FSELevelFilter UPROPERTY(SaveGame) FSEActorClassFilter ActorFilter; - UPROPERTY(SaveGame) - FSEActorClassFilter LoadActorFilter; - - UPROPERTY(SaveGame) - bool bStoreComponents = false; - UPROPERTY(SaveGame) FSEComponentClassFilter ComponentFilter; - UPROPERTY(SaveGame) - FSEComponentClassFilter LoadComponentFilter; - FSELevelFilter() = default; - void FromSlot(const USaveSlot& Slot); - void BakeAllowedClasses() const; - bool ShouldSave(const AActor* Actor) const; - bool ShouldSave(const UActorComponent* Component) const; - bool ShouldLoad(const AActor* Actor) const; - bool ShouldLoad(const UActorComponent* Component) const; + bool Stores(const AActor* Actor) const; + bool StoresAnyComponents() const; + bool Stores(const UActorComponent* Component) const; static bool StoresTransform(const UActorComponent* Component); static bool StoresTags(const UActorComponent* Component); diff --git a/Source/SaveExtension/Public/Misc/ClassFilter.h b/Source/SaveExtension/Public/Misc/ClassFilter.h index 06e1b16..7db8940 100644 --- a/Source/SaveExtension/Public/Misc/ClassFilter.h +++ b/Source/SaveExtension/Public/Misc/ClassFilter.h @@ -45,13 +45,18 @@ struct SAVEEXTENSION_API FSEClassFilter /** Bakes a set of allowed classes based on the current settings */ void BakeAllowedClasses() const; - FORCEINLINE bool IsClassAllowed(UClass* const Class) const + bool IsAllowed(UClass* Class) const { // Check is a single O(1) pointer hash comparison return BakedAllowedClasses.Contains(Class); } - FORCEINLINE UClass* GetBaseClass() const + bool IsAnyAllowed() const + { + return BakedAllowedClasses.Num() > 0; + } + + UClass* GetBaseClass() const { return BaseClass; } @@ -64,50 +69,20 @@ struct SAVEEXTENSION_API FSEClassFilter USTRUCT(BlueprintType) -struct FSEActorClassFilter +struct FSEActorClassFilter : public FSEClassFilter { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Filter) - FSEClassFilter ClassFilter; - - - FSEActorClassFilter() : ClassFilter(AActor::StaticClass()) {} - FSEActorClassFilter(TSubclassOf actorClass) : ClassFilter(actorClass) {} - - /** Bakes a set of allowed classes based on the current settings */ - void BakeAllowedClasses() const - { - ClassFilter.BakeAllowedClasses(); - } - - FORCEINLINE bool IsClassAllowed(UClass* const Class) const - { - return ClassFilter.IsClassAllowed(Class); - } + FSEActorClassFilter() : Super(AActor::StaticClass()) {} + FSEActorClassFilter(TSubclassOf ActorClass) : Super(ActorClass) {} }; USTRUCT(BlueprintType) -struct FSEComponentClassFilter +struct FSEComponentClassFilter : public FSEClassFilter { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Filter) - FSEClassFilter ClassFilter; - - - FSEComponentClassFilter() : ClassFilter(UActorComponent::StaticClass()) {} - FSEComponentClassFilter(TSubclassOf compClass) : ClassFilter(compClass) {} - - /** Bakes a set of allowed classes based on the current settings */ - void BakeAllowedClasses() const - { - ClassFilter.BakeAllowedClasses(); - } - - FORCEINLINE bool IsClassAllowed(UClass* const Class) const - { - return ClassFilter.IsClassAllowed(Class); - } + FSEComponentClassFilter() : Super(UActorComponent::StaticClass()) {} + FSEComponentClassFilter(TSubclassOf CompClass) : Super(CompClass) {} }; diff --git a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h index 5d0d6e7..05b7c76 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h @@ -3,6 +3,7 @@ #pragma once #include "SaveFileHelpers.h" +#include "SaveManager.h" #include @@ -17,7 +18,6 @@ class FLoadFileTask : public FNonAbandonableTask const FString SlotName; TWeakObjectPtr Slot; - TWeakObjectPtr SlotData; public: @@ -29,9 +29,9 @@ class FLoadFileTask : public FNonAbandonableTask { Slot->ClearInternalFlags(EInternalObjectFlags::Async); } - if (SlotData.IsValid()) + if (IsValid(GetData())) { - SlotData->ClearInternalFlags(EInternalObjectFlags::Async); + GetData()->ClearInternalFlags(EInternalObjectFlags::Async); } } @@ -49,14 +49,14 @@ class FLoadFileTask : public FNonAbandonableTask } } - USaveSlot* GetInfo() + USaveSlot* GetInfo() const { return Slot.Get(); } - USaveSlotData* GetData() + USaveSlotData* GetData() const { - return SlotData.Get(); + return Slot.IsValid()? Slot->GetData() : nullptr; } TStatId GetStatId() const diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index ff663b6..eb6392e 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -9,7 +9,7 @@ #include "SaveExtensionInterface.h" #include "SaveSlot.h" #include "SaveSlotData.h" -#include "Serialization/SlotDataTask.h" +#include "Serialization/SEDataTask.h" #include #include @@ -69,7 +69,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi { GENERATED_BODY() - friend USaveSlotDataTask; + friend FSEDataTask; /************************************************************************/ @@ -96,8 +96,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UPROPERTY(Transient) TArray> SubscribedInterfaces; - UPROPERTY(Transient) - TArray Tasks; + TArray> Tasks; /************************************************************************/ @@ -360,15 +359,15 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi void OnLevelLoaded(ULevelStreaming* StreamingLevel) {} - USaveSlotDataTask* CreateTask(TSubclassOf TaskType); - - template - TaskType* CreateTask() + template + TaskType& CreateTask() { - return Cast(CreateTask(TaskType::StaticClass())); + return static_cast( + *Tasks.Add_GetRef(MakeUnique(this, ActiveSlot)) + ); } - void FinishTask(USaveSlotDataTask* Task); + void FinishTask(FSEDataTask* Task); public: bool HasTasks() const @@ -417,10 +416,10 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(Category = SaveExtension, BlueprintCallable) void UnsubscribeFromEvents(const TScriptInterface& Interface); - void OnSaveBegan(const FSELevelFilter& Filter); - void OnSaveFinished(const FSELevelFilter& Filter, const bool bError); - void OnLoadBegan(const FSELevelFilter& Filter); - void OnLoadFinished(const FSELevelFilter& Filter, const bool bError); + void OnSaveBegan(); + void OnSaveFinished(const bool bError); + void OnLoadBegan(); + void OnLoadFinished(const bool bError); private: void OnMapLoadStarted(const FString& MapName); diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index af2a644..87c38e7 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -17,9 +17,9 @@ struct FSELevelFilter; * Specifies the behavior while saving or loading */ UENUM() -enum class ESaveASyncMode : uint8 +enum class ESEAsyncMode : uint8 { - OnlySync, + SaveAndLoadSync, LoadAsync, SaveAsync, SaveAndLoadAsync @@ -101,31 +101,9 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") FSEActorClassFilter ActorFilter; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (PinHiddenByDefault, InlineEditConditionToggle)) - bool bUseLoadActorFilter = false; - - /** If enabled, this filter will be used while loading instead of "ActorFilter" */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (EditCondition = "bUseLoadActorFilter")) - FSEActorClassFilter LoadActorFilter; - - /** If true will store ActorComponents depending on the filters */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") - bool bStoreComponents = true; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") FSEComponentClassFilter ComponentFilter; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (PinHiddenByDefault, InlineEditConditionToggle)) - bool bUseLoadComponentFilter = false; - - /** If enabled, this filter will be used while loading instead of "ComponentFilter" */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (EditCondition = "bUseLoadComponentFilter")) - FSEComponentClassFilter LoadComponentFilter; - /** If true, will Save and Load levels when they are shown or hidden. * This includes level streaming and world composition. */ @@ -134,13 +112,13 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame /** Serialization will be multi-threaded between all available cores. */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") - ESaveASyncMode MultithreadedSerialization = ESaveASyncMode::SaveAsync; + ESEAsyncMode MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; /** Split serialization between multiple frames. Ignored if MultithreadedSerialization is used * Currently only implemented on Loading */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") - ESaveASyncMode FrameSplittedSerialization = ESaveASyncMode::OnlySync; + ESEAsyncMode FrameSplittedSerialization = ESEAsyncMode::SaveAndLoadSync; /** Max milliseconds to use every frame in an asynchronous operation. * If running at 60Fps (16.6ms), loading or saving asynchronously will add MaxFrameMS: @@ -148,13 +126,12 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame * This means gameplay will not be stopped nor have frame drops while saving or loading. Works best for * non multi-threaded platforms */ - UPROPERTY( - EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async", meta = (UIMin = "3", UIMax = "10")) + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async", meta = (UIMin = "3", UIMax = "10")) float MaxFrameMs = 5.f; /** Files will be loaded or saved on a secondary thread while game continues */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") - ESaveASyncMode MultithreadedFiles = ESaveASyncMode::SaveAndLoadAsync; + ESEAsyncMode MultithreadedFiles = ESEAsyncMode::SaveAndLoadAsync; /** * If checked, will print messages to Log, and Viewport if DebugInScreen is enabled. @@ -251,16 +228,10 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame virtual int32 OnGetIndex() const; public: - UFUNCTION(BlueprintPure, Category = SaveSlot) - const FSEActorClassFilter& GetActorFilter(bool bIsLoading) const; - - UFUNCTION(BlueprintPure, Category = SaveSlot) - const FSEComponentClassFilter& GetComponentFilter(bool bIsLoading) const; - bool IsMTSerializationLoad() const; bool IsMTSerializationSave() const; - ESaveASyncMode GetFrameSplitSerialization() const; + ESEAsyncMode GetFrameSplitSerialization() const; float GetMaxFrameMs() const; bool IsFrameSplitLoad() const; @@ -269,5 +240,11 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame bool IsMTFilesLoad() const; bool IsMTFilesSave() const; - FSELevelFilter ToFilter() const; + // Called for every level before being saved or loaded + UFUNCTION(BlueprintNativeEvent, Category = Slot) + void GetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) const; + +private: + // Called for every level before being saved or loaded + virtual void OnGetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) const; }; diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index bd60669..89bb26e 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -45,8 +45,7 @@ class SAVEEXTENSION_API USaveSlotData : public USaveGame bool bStoreGameInstance = false; FObjectRecord GameInstance; - FSELevelFilter GlobalLevelFilter; - FPersistentLevelRecord MainLevel; + FPersistentLevelRecord RootLevel; TArray SubLevels; diff --git a/Source/SaveExtension/Public/Serialization/LevelRecords.h b/Source/SaveExtension/Public/Serialization/LevelRecords.h index 9be4385..232adae 100644 --- a/Source/SaveExtension/Public/Serialization/LevelRecords.h +++ b/Source/SaveExtension/Public/Serialization/LevelRecords.h @@ -19,16 +19,17 @@ struct FLevelRecord : public FBaseRecord { GENERATED_BODY() - bool bOverrideGlobalFilter = false; - // Filter is used if bOverrideGlobalFilter is true - FSELevelFilter Filter; - /** Record of the Level Script Actor */ FActorRecord LevelScript; /** Records of the World Actors */ TArray Actors; + /** Not-serialized. Assigned before loading and saving by the SaveSlot */ + FSELevelFilter Filter; + + /** Not-serialized. During saving or loading points to the live actor */ + TArray>> RecordsToActors; FLevelRecord() : Super() {} diff --git a/Source/SaveExtension/Public/Serialization/MTTask.h b/Source/SaveExtension/Public/Serialization/MTTask.h index 2f19814..f480bfa 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask.h +++ b/Source/SaveExtension/Public/Serialization/MTTask.h @@ -16,19 +16,14 @@ // Async task to serialize actors from a level. class FMTTask : public FNonAbandonableTask { -protected: +public: /** Used only if Sync */ - const UWorld* const World; - USaveSlotData* SlotData; + UWorld* const World = nullptr; + USaveSlotData* SlotData = nullptr; - // Locally cached settings - const FSELevelFilter& Filter; - - FMTTask( - const bool bIsloading, const UWorld* InWorld, USaveSlotData* InSlotData, const FSELevelFilter& Filter) - : World(InWorld) - , SlotData(InSlotData) - , Filter(Filter) + FMTTask(const bool bIsloading, UWorld* World, USaveSlotData* SlotData) + : World(World) + , SlotData(SlotData) {} }; diff --git a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h index 03de370..638ae1d 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h +++ b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h @@ -27,30 +27,30 @@ DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); // Async task to serialize actors from a level. class FMTTask_SerializeActors : public FMTTask { - const TArray* const LevelActors; - const int32 StartIndex; - const int32 Num; + const TArray* LevelActors; + const int32 StartIndex = 0; + const int32 Num = 0; const bool bStoreGameInstance = false; - /** USE ONLY FOR DUMPING DATA */ FLevelRecord* LevelRecord = nullptr; + const FSELevelFilter* Filter = nullptr; + FActorRecord LevelScriptRecord; TArray ActorRecords; public: - FMTTask_SerializeActors(const UWorld* World, USaveSlotData* SlotData, - const TArray* const InLevelActors, const int32 InStartIndex, const int32 InNum, - bool bStoreGameInstance, FLevelRecord* InLevelRecord, const FSELevelFilter& Filter) - : FMTTask(false, World, SlotData, Filter) - , LevelActors(InLevelActors) - , StartIndex(InStartIndex) - , Num(InNum) + FMTTask_SerializeActors(UWorld* World, USaveSlotData* SlotData, + const TArray* LevelActors, const int32 StartIndex, const int32 Num, + bool bStoreGameInstance, FLevelRecord* LevelRecord, const FSELevelFilter* Filter) + : FMTTask(false, World, SlotData) + , LevelActors(LevelActors) + , StartIndex(StartIndex) + , Num(Num) , bStoreGameInstance(bStoreGameInstance) - , LevelRecord(InLevelRecord) - , LevelScriptRecord{} - , ActorRecords{} + , LevelRecord(LevelRecord) + , Filter(Filter) { // No apparent performance benefit // ActorRecords.Reserve(Num); diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index a0fdfdd..8c3704f 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -41,7 +41,7 @@ struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 0; + return !Name.IsNone() && Class; } - FORCEINLINE bool operator==(const UObject* Other) const + bool operator==(const UObject* Other) const { return Other && Name == Other->GetFName() && Class == Other->GetClass(); } diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask.h b/Source/SaveExtension/Public/Serialization/SEDataTask.h similarity index 66% rename from Source/SaveExtension/Public/Serialization/SlotDataTask.h rename to Source/SaveExtension/Public/Serialization/SEDataTask.h index 8e8172f..23d70ee 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask.h @@ -10,42 +10,44 @@ #include #include -#include "SlotDataTask.generated.h" - class USaveManager; +enum class ESETaskType : uint8 +{ + None, + Load, + Save +}; /** - * Base class for managing a SaveData file + * Base class for managing the data of SaveSlot file */ -UCLASS() -class USaveSlotDataTask : public UObject +struct FSEDataTask { - GENERATED_BODY() - + ESETaskType Type = ESETaskType::None; private: - uint8 bRunning : 1; - uint8 bFinished : 1; - uint8 bSucceeded : 1; + bool bRunning = false; + bool bFinished = false; + bool bSucceeded = false; protected: - UPROPERTY() - USaveSlotData* SlotData; - UPROPERTY() + TObjectPtr Manager; + TObjectPtr SlotData; float MaxFrameMs = 0.f; -public: - USaveSlotDataTask() : Super(), bRunning(false), bFinished(false) {} - void Prepare(USaveSlot* Slot) - { - SlotData = Slot->GetData(); - MaxFrameMs = Slot->GetMaxFrameMs(); - } +public: + FSEDataTask(USaveManager* Manager, USaveSlot* Slot, ESETaskType Type) + : Type(Type) + , Manager(Manager) + , SlotData(Slot->GetData()) + , MaxFrameMs(Slot->GetMaxFrameMs()) + {} + virtual ~FSEDataTask() = default; - USaveSlotDataTask* Start(); + FSEDataTask& Start(); virtual void Tick(float DeltaTime) {} @@ -76,23 +78,16 @@ class USaveSlotDataTask : public UObject virtual void OnFinish(bool bSuccess) {} - USaveManager* GetManager() const; - void BakeAllFilters(); - const FSELevelFilter& GetGlobalFilter() const; - const FSELevelFilter& GetLevelFilter(const FLevelRecord& Level) const; - FLevelRecord* FindLevelRecord(const ULevelStreaming* Level) const; - //~ Begin UObject Interface - virtual UWorld* GetWorld() const override; - //~ End UObject Interface - - FORCEINLINE float GetTimeMilliseconds() const + float GetTimeMilliseconds() const { return FPlatformTime::ToMilliseconds(FPlatformTime::Cycles()); } + + UWorld* GetWorld() const; }; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h similarity index 73% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index 39c27ce..d7c7c6f 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -7,7 +7,7 @@ #include "Multithreading/LoadFileTask.h" #include "SaveSlot.h" #include "SaveSlotData.h" -#include "SlotDataTask.h" +#include "SEDataTask.h" #include #include @@ -15,8 +15,6 @@ #include #include -#include "SlotDataTask_Loader.generated.h" - enum class ELoadDataTaskState : uint8 { @@ -33,16 +31,12 @@ enum class ELoadDataTaskState : uint8 /** * Manages the loading process of a SaveData file */ -UCLASS() -class USaveSlotDataTask_Loader : public USaveSlotDataTask +struct FSEDataTask_Load : public FSEDataTask { - GENERATED_BODY() - protected: FName SlotName; - UPROPERTY() - USaveSlot* Slot; + TObjectPtr Slot; FOnGameLoaded Delegate; @@ -62,18 +56,21 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask public: - USaveSlotDataTask_Loader() : Super() {} + FSEDataTask_Load(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask(Manager, Slot, ESETaskType::Load) + {} + ~FSEDataTask_Load(); - auto Setup(FName InSlotName) + auto& Setup(FName InSlotName) { SlotName = InSlotName; - return this; + return *this; } - auto Bind(const FOnGameLoaded& OnLoaded) + auto& Bind(const FOnGameLoaded& OnLoaded) { Delegate = OnLoaded; - return this; + return *this; } void OnMapLoaded(); @@ -83,19 +80,18 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask virtual void Tick(float DeltaTime) override; virtual void OnFinish(bool bSuccess) override; - virtual void BeginDestroy() override; void StartDeserialization(); /** Spawns Actors hat were saved but which actors are not in the world. */ - void RespawnActors(const TArray& Records, const ULevel* Level); + void RespawnActors(const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord); protected: //~ Begin Files void StartLoadingData(); USaveSlotData* GetLoadedData() const; - FORCEINLINE const bool IsDataLoaded() const + const bool IsDataLoaded() const { return LoadDataTask && LoadDataTask->IsDone(); }; @@ -117,22 +113,17 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask void PrepareAllLevels(); void PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord); - /** Deserializes all Level actors. */ - inline void DeserializeLevel_Actor( - AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter); - void FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const; -private: /** Deserializes Game Instance Object and its Properties. - Requires 'SaveGameMode' flag to be used. */ + * Requires 'SaveGameInstance' flag to be used. + */ void DeserializeGameInstance(); /** Serializes an actor into this Actor Record */ - bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter); + bool DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord); /** Deserializes the components of an actor from a provided Record */ - void DeserializeActorComponents( - AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 indent = 0); + void DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 indent = 0); /** END Deserialization */ }; \ No newline at end of file diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h b/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h similarity index 52% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h index dbb7a13..f2a5665 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h @@ -3,28 +3,26 @@ #pragma once #include "ISaveExtension.h" -#include "SlotDataTask_Loader.h" - -#include "SlotDataTask_LevelLoader.generated.h" +#include "SEDataTask_Load.h" /** * Manages the serializing process of a single level */ -UCLASS() -class USaveSlotDataTask_LevelLoader : public USaveSlotDataTask_Loader +struct FSEDataTask_LoadLevel : public FSEDataTask_Load { - GENERATED_BODY() - + TObjectPtr StreamingLevel; - UPROPERTY() - ULevelStreaming* StreamingLevel; public: - auto Setup(ULevelStreaming* InStreamingLevel) + FSEDataTask_LoadLevel(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask_Load(Manager, Slot) + {} + + auto& Setup(ULevelStreaming* InStreamingLevel) { StreamingLevel = InStreamingLevel; - return this; + return *this; } private: diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h similarity index 77% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 529ae76..a999da7 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -7,7 +7,7 @@ #include "MTTask_SerializeActors.h" #include "Multithreading/SaveFileTask.h" #include "SaveSlotData.h" -#include "SlotDataTask.h" +#include "SEDataTask.h" #include #include @@ -17,46 +17,42 @@ #include #include -#include "SlotDataTask_Saver.generated.h" - /** * Manages the saving process of a SaveData file */ -UCLASS() -class USaveSlotDataTask_Saver : public USaveSlotDataTask +struct FSEDataTask_Save : public FSEDataTask { - GENERATED_BODY() - - bool bOverride; - bool bSaveThumbnail; + bool bOverride = false; + bool bSaveThumbnail = false; FName SlotName; - int32 Width; - int32 Height; + int32 Width = 0; + int32 Height = 0; FOnGameSaved Delegate; protected: - UPROPERTY() - USaveSlot* Slot; + TObjectPtr Slot; /** Start Async variables */ TWeakObjectPtr CurrentLevel; TWeakObjectPtr CurrentSLevel; - int32 CurrentActorIndex; TArray> CurrentLevelActors; /** End Async variables */ /** Begin AsyncTasks */ TArray> Tasks; - FAsyncTask* SaveTask; + FAsyncTask* SaveTask = nullptr; /** End AsyncTasks */ public: - USaveSlotDataTask_Saver() : USaveSlotDataTask(), SaveTask(nullptr) {} + FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask(Manager, Slot, ESETaskType::Save) + {} + ~FSEDataTask_Save(); - auto* Setup( + auto& Setup( FName InSlotName, bool bInOverride, bool bInSaveThumbnail, const int32 InWidth, const int32 InHeight) { SlotName = InSlotName; @@ -65,20 +61,19 @@ class USaveSlotDataTask_Saver : public USaveSlotDataTask Width = InWidth; Height = InHeight; - return this; + return *this; } - auto* Bind(const FOnGameSaved& OnSaved) + auto& Bind(const FOnGameSaved& OnSaved) { Delegate = OnSaved; - return this; + return *this; } // Where all magic happens virtual void OnStart() override; virtual void Tick(float DeltaTime) override; virtual void OnFinish(bool bSuccess) override; - virtual void BeginDestroy() override; protected: /** BEGIN Serialization */ diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h b/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h similarity index 55% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h index 8477509..ede2300 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h @@ -3,28 +3,26 @@ #pragma once #include "ISaveExtension.h" -#include "SlotDataTask_Saver.h" - -#include "SlotDataTask_LevelSaver.generated.h" +#include "SEDataTask_Save.h" /** * Manages the serializing process of a single level */ -UCLASS() -class USaveSlotDataTask_LevelSaver : public USaveSlotDataTask_Saver +struct FSEDataTask_SaveLevel : public FSEDataTask_Save { - GENERATED_BODY() - + TObjectPtr StreamingLevel; - UPROPERTY() - ULevelStreaming* StreamingLevel; public: - auto Setup(ULevelStreaming* InStreamingLevel) + FSEDataTask_SaveLevel(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask_Save(Manager, Slot) + {} + + auto& Setup(ULevelStreaming* InStreamingLevel) { StreamingLevel = InStreamingLevel; - return this; + return *this; } private: diff --git a/Source/Test/Private/Files.spec.cpp b/Source/Test/Private/Files.spec.cpp index 37f281f..97da352 100644 --- a/Source/Test/Private/Files.spec.cpp +++ b/Source/Test/Private/Files.spec.cpp @@ -34,11 +34,11 @@ void FSaveSpec_Files::Define() SaveManager->bTickWithGameWorld = true; - SaveManager->GetActiveSlot()->MultithreadedSerialization = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; }); It("Can save files synchronously", [this]() { - SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; TestTrue("Saved", SaveManager->SaveSlot(0)); @@ -46,7 +46,7 @@ void FSaveSpec_Files::Define() }); It("Can save files asynchronously", [this]() { - SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::SaveAsync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESEAsyncMode::SaveAsync; bFinishTick = false; bool bSaving = @@ -66,7 +66,7 @@ void FSaveSpec_Files::Define() }); It("Can load files synchronously", [this]() { - SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; TestTrue("Saved", SaveManager->SaveSlot(0)); diff --git a/Source/Test/Private/GameInstanceSpec.h b/Source/Test/Private/GameInstanceSpec.h index 92e7fca..afe795c 100644 --- a/Source/Test/Private/GameInstanceSpec.h +++ b/Source/Test/Private/GameInstanceSpec.h @@ -17,7 +17,7 @@ class UTestSaveSlot : public USaveSlot { bStoreGameInstance = true; - MultithreadedFiles = ESaveASyncMode::OnlySync; - MultithreadedSerialization = ESaveASyncMode::OnlySync; + MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; + MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; } }; diff --git a/Source/Test/Private/SavingSpec.h b/Source/Test/Private/SavingSpec.h index f16b5e0..92b07b7 100644 --- a/Source/Test/Private/SavingSpec.h +++ b/Source/Test/Private/SavingSpec.h @@ -17,8 +17,8 @@ class UTestSaveSlot_SyncSaving : public USaveSlot { bStoreGameInstance = true; - MultithreadedFiles = ESaveASyncMode::OnlySync; - MultithreadedSerialization = ESaveASyncMode::OnlySync; - ActorFilter.ClassFilter.AllowedClasses.Add(ATestActor::StaticClass()); + MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; + MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; + ActorFilter.AllowedClasses.Add(ATestActor::StaticClass()); } }; From 565759a42c5858e6598dfa9d0a13a0afa4d51127 Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 29 Sep 2023 18:14:37 +0200 Subject: [PATCH 6/7] Multiple fixes to filters --- SaveExtension.uplugin | 19 +- .../SEActorClassFilterCustomization.cpp | 17 - .../SEActorClassFilterCustomization.h | 25 -- .../SEClassFilterCustomization.cpp | 17 +- .../SEClassFilterCustomization.h | 6 - .../SEComponentClassFilterCustomization.cpp | 17 - .../SEComponentClassFilterCustomization.h | 25 -- .../Customizations/SaveSlotDetails.cpp | 4 +- Source/Editor/Private/SaveExtensionEditor.cpp | 12 +- Source/SaveExtension/Private/LevelFilter.cpp | 28 +- .../Private/LifetimeComponent.cpp | 4 +- .../Private/Multithreading/LoadFileTask.cpp | 43 ++- .../Private/Multithreading/LoadSlotsTask.cpp | 6 +- .../SaveExtension/Private/SaveFileHelpers.cpp | 56 ++- Source/SaveExtension/Private/SaveManager.cpp | 74 ++-- Source/SaveExtension/Private/SaveSlot.cpp | 59 +-- Source/SaveExtension/Private/SaveSlotData.cpp | 10 +- .../Private/Serialization/LevelRecords.cpp | 7 - .../Serialization/MTTask_SerializeActors.cpp | 38 +- .../Private/Serialization/SEDataTask.cpp | 52 +++ ...ataTask_Loader.cpp => SEDataTask_Load.cpp} | 338 ++++++++---------- .../Serialization/SEDataTask_LoadLevel.cpp | 72 ++++ ...DataTask_Saver.cpp => SEDataTask_Save.cpp} | 95 +++-- ...evelSaver.cpp => SEDataTask_SaveLevel.cpp} | 6 +- .../Private/Serialization/SlotDataTask.cpp | 91 ----- .../SlotDataTask_LevelLoader.cpp | 64 ---- Source/SaveExtension/Public/LevelFilter.h | 18 +- .../SaveExtension/Public/Misc/ClassFilter.h | 51 +-- .../Public/Multithreading/LoadFileTask.h | 41 +-- Source/SaveExtension/Public/SaveFileHelpers.h | 6 +- Source/SaveExtension/Public/SaveManager.h | 27 +- Source/SaveExtension/Public/SaveSlot.h | 69 ++-- Source/SaveExtension/Public/SaveSlotData.h | 14 +- .../Public/Serialization/LevelRecords.h | 9 +- .../Public/Serialization/MTTask.h | 17 +- .../Serialization/MTTask_SerializeActors.h | 28 +- .../Public/Serialization/Records.h | 6 +- .../{SlotDataTask.h => SEDataTask.h} | 57 ++- ...lotDataTask_Loader.h => SEDataTask_Load.h} | 43 +-- ...k_LevelLoader.h => SEDataTask_LoadLevel.h} | 20 +- ...SlotDataTask_Saver.h => SEDataTask_Save.h} | 38 +- ...sk_LevelSaver.h => SEDataTask_SaveLevel.h} | 20 +- Source/Test/Private/Files.spec.cpp | 8 +- Source/Test/Private/GameInstanceSpec.h | 4 +- Source/Test/Private/SavingSpec.h | 6 +- 45 files changed, 692 insertions(+), 975 deletions(-) delete mode 100644 Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp delete mode 100644 Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h delete mode 100644 Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp delete mode 100644 Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h create mode 100644 Source/SaveExtension/Private/Serialization/SEDataTask.cpp rename Source/SaveExtension/Private/Serialization/{SlotDataTask_Loader.cpp => SEDataTask_Load.cpp} (55%) create mode 100644 Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp rename Source/SaveExtension/Private/Serialization/{SlotDataTask_Saver.cpp => SEDataTask_Save.cpp} (74%) rename Source/SaveExtension/Private/Serialization/{SlotDataTask_LevelSaver.cpp => SEDataTask_SaveLevel.cpp} (79%) delete mode 100644 Source/SaveExtension/Private/Serialization/SlotDataTask.cpp delete mode 100644 Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp rename Source/SaveExtension/Public/Serialization/{SlotDataTask.h => SEDataTask.h} (66%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_Loader.h => SEDataTask_Load.h} (73%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_LevelLoader.h => SEDataTask_LoadLevel.h} (52%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_Saver.h => SEDataTask_Save.h} (76%) rename Source/SaveExtension/Public/Serialization/{SlotDataTask_LevelSaver.h => SEDataTask_SaveLevel.h} (55%) diff --git a/SaveExtension.uplugin b/SaveExtension.uplugin index 40c8e8b..3db46d6 100644 --- a/SaveExtension.uplugin +++ b/SaveExtension.uplugin @@ -21,13 +21,10 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", - "Linux", + "Mac", + "IOS", "Android", - "PS5", - "XboxOne", - "Switch", - "Mac" + "Linux" ] }, { @@ -36,13 +33,10 @@ "LoadingPhase": "PostEngineInit", "WhitelistPlatforms": [ "Win64", - "Win32", - "Linux", + "Mac", + "IOS", "Android", - "PS5", - "XboxOne", - "Switch", - "Mac" + "Linux" ] }, { @@ -51,7 +45,6 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", "Linux", "Mac" ] diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp deleted file mode 100644 index ac3f992..0000000 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Customizations/SEActorClassFilterCustomization.h" - -#include -#include - -#define LOCTEXT_NAMESPACE "FSEActorClassFilterCustomization" - - -TSharedPtr FSEActorClassFilterCustomization::GetFilterHandle( - TSharedRef StructPropertyHandle) -{ - return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEActorClassFilter, ClassFilter)); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h deleted file mode 100644 index 9078c45..0000000 --- a/Source/Editor/Private/Customizations/SEActorClassFilterCustomization.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. -#pragma once - -#include "SEClassFilterCustomization.h" - - -class IPropertyHandle; - -class FSEActorClassFilterCustomization : public FSEClassFilterCustomization -{ -public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ - static TSharedRef MakeInstance() - { - return MakeShared(); - } - -protected: - virtual TSharedPtr GetFilterHandle( - TSharedRef StructPropertyHandle) override; -}; diff --git a/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp index 4f19d84..b9d0980 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp +++ b/Source/Editor/Private/Customizations/SEClassFilterCustomization.cpp @@ -25,7 +25,6 @@ void FSEClassFilterCustomization::CustomizeHeader(TSharedRef RawStructData; - FilterHandle->AccessRawData(RawStructData); + StructHandle->AccessRawData(RawStructData); TArray Outers; - FilterHandle->GetOuterObjects(Outers); + StructHandle->GetOuterObjects(Outers); UObject* FirstOuter = Outers.Num() ? Outers[0] : nullptr; for (int32 ContainerIdx = 0; ContainerIdx < RawStructData.Num(); ++ContainerIdx) @@ -98,19 +97,19 @@ void FSEClassFilterCustomization::BuildEditableFilterList() TSharedRef FSEClassFilterCustomization::GetListContent() { - if (!FilterHandle.IsValid() || FilterHandle->GetProperty() == nullptr) + if (!StructHandle.IsValid() || StructHandle->GetProperty() == nullptr) { return SNullWidget::NullWidget; } - bool bReadOnly = FilterHandle->IsEditConst(); + bool bReadOnly = StructHandle->IsEditConst(); // clang-format off TSharedRef EditPopup = SNew(SClassFilter, EditableFilters) .ReadOnly(bReadOnly) .OnFilterChanged(this, &FSEClassFilterCustomization::RefreshClassList) - .PropertyHandle(FilterHandle); + .PropertyHandle(StructHandle); LastFilterPopup = EditPopup; return SNew(SVerticalBox) + SVerticalBox::Slot() @@ -137,12 +136,12 @@ void FSEClassFilterCustomization::OnPopupStateChanged(bool bIsOpened) void FSEClassFilterCustomization::OnClearClicked() { FScopedTransaction Transaction(LOCTEXT("ClassFilter_Filter", "Clear Filter")); - FilterHandle->NotifyPreChange(); + StructHandle->NotifyPreChange(); for (auto& Filter : EditableFilters) { *Filter.Filter = {}; } - FilterHandle->NotifyPostChange(EPropertyChangeType::ValueSet); + StructHandle->NotifyPostChange(EPropertyChangeType::ValueSet); RefreshClassList(); } diff --git a/Source/Editor/Private/Customizations/SEClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEClassFilterCustomization.h index ed46b6c..fb9f20d 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterCustomization.h +++ b/Source/Editor/Private/Customizations/SEClassFilterCustomization.h @@ -26,7 +26,6 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE TArray> PreviewClasses; TSharedPtr StructHandle; - TSharedPtr FilterHandle; TSharedPtr EditButton; @@ -61,11 +60,6 @@ class FSEClassFilterCustomization : public IPropertyTypeCustomization, public FE virtual void PostRedo(bool bSuccess) override; //~ End FEditorUndoClient Interface - virtual TSharedPtr GetFilterHandle(TSharedRef StructPropertyHandle) - { - return StructHandle; - } - /** Build List of Editable Containers */ void BuildEditableFilterList(); diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp deleted file mode 100644 index c0e79af..0000000 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Customizations/SEComponentClassFilterCustomization.h" - -#include "PropertyHandle.h" - - -#define LOCTEXT_NAMESPACE "FSEComponentClassFilterCustomization" - - -TSharedPtr FSEComponentClassFilterCustomization::GetFilterHandle( - TSharedRef StructPropertyHandle) -{ - return StructHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FSEComponentClassFilter, ClassFilter)); -} - -#undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h b/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h deleted file mode 100644 index 16a632f..0000000 --- a/Source/Editor/Private/Customizations/SEComponentClassFilterCustomization.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. -#pragma once - -#include "SEClassFilterCustomization.h" - - -class IPropertyHandle; - -class FSEComponentClassFilterCustomization : public FSEClassFilterCustomization -{ -public: - /** - * Creates a new instance. - * - * @return A new struct customization for Factions. - */ - static TSharedRef MakeInstance() - { - return MakeShared(); - } - -protected: - virtual TSharedPtr GetFilterHandle( - TSharedRef StructPropertyHandle) override; -}; diff --git a/Source/Editor/Private/Customizations/SaveSlotDetails.cpp b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp index 9ed50fb..7133195 100644 --- a/Source/Editor/Private/Customizations/SaveSlotDetails.cpp +++ b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp @@ -71,7 +71,7 @@ EVisibility FSaveSlotDetails::GetWarningVisibility() const { if (Slot.IsValid()) { - return Slot->GetFrameSplitSerialization() == ESaveASyncMode::OnlySync ? EVisibility::Collapsed + return Slot->GetFrameSplitSerialization() == ESEAsyncMode::SaveAndLoadSync ? EVisibility::Collapsed : EVisibility::Visible; } return EVisibility::Collapsed; @@ -81,7 +81,7 @@ bool FSaveSlotDetails::CanEditAsynchronous() const { if (Slot.IsValid()) { - return Slot->GetFrameSplitSerialization() != ESaveASyncMode::OnlySync; + return Slot->GetFrameSplitSerialization() != ESEAsyncMode::SaveAndLoadSync; } return true; } diff --git a/Source/Editor/Private/SaveExtensionEditor.cpp b/Source/Editor/Private/SaveExtensionEditor.cpp index cf68f32..9f648e0 100644 --- a/Source/Editor/Private/SaveExtensionEditor.cpp +++ b/Source/Editor/Private/SaveExtensionEditor.cpp @@ -4,10 +4,8 @@ #include "Asset/AssetTypeAction_SaveSlot.h" #include "Asset/AssetTypeAction_SaveSlotData.h" -#include "Customizations/SEActorClassFilterCustomization.h" #include "Customizations/SEClassFilterCustomization.h" #include "Customizations/SEClassFilterGraphPanelPinFactory.h" -#include "Customizations/SEComponentClassFilterCustomization.h" #include "Customizations/SaveSlotDetails.h" #include "Kismet2/KismetEditorUtilities.h" @@ -48,12 +46,10 @@ void FSaveExtensionEditor::RegisterPropertyTypeCustomizations() RegisterCustomPropertyTypeLayout("SEClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout( - "SEActorClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( - &FSEActorClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout( - "SEComponentClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic( - &FSEComponentClassFilterCustomization::MakeInstance)); + RegisterCustomPropertyTypeLayout("SEActorClassFilter", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); + RegisterCustomPropertyTypeLayout("SEComponentClassFilter", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); RegisterCustomPinFactory(); } diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index e7247c5..53c5f85 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -13,42 +13,26 @@ const FName FSELevelFilter::TagNoPhysics{"!SavePhysics"}; const FName FSELevelFilter::TagNoTags{"!SaveTags"}; const FName FSELevelFilter::TagTransform{"SaveTransform"}; -void FSELevelFilter::FromSlot(const USaveSlot& Slot) -{ - ActorFilter = Slot.GetActorFilter(true); - LoadActorFilter = Slot.GetActorFilter(false); - bStoreComponents = Slot.bStoreComponents; - ComponentFilter = Slot.GetComponentFilter(true); - LoadComponentFilter = Slot.GetComponentFilter(false); -} void FSELevelFilter::BakeAllowedClasses() const { TRACE_CPUPROFILER_EVENT_SCOPE(FSELevelFilter::BakeAllowedClasses); ActorFilter.BakeAllowedClasses(); ComponentFilter.BakeAllowedClasses(); - LoadActorFilter.BakeAllowedClasses(); - LoadComponentFilter.BakeAllowedClasses(); -} - -bool FSELevelFilter::ShouldSave(const AActor* Actor) const -{ - return ActorFilter.IsClassAllowed(Actor->GetClass()); } -bool FSELevelFilter::ShouldSave(const UActorComponent* Component) const +bool FSELevelFilter::Stores(const AActor* Actor) const { - return IsValid(Component) && ComponentFilter.IsClassAllowed(Component->GetClass()); + return ActorFilter.IsAllowed(Actor->GetClass()); } -bool FSELevelFilter::ShouldLoad(const AActor* Actor) const +bool FSELevelFilter::StoresAnyComponents() const { - return LoadActorFilter.IsClassAllowed(Actor->GetClass()); + return ComponentFilter.IsAnyAllowed(); } - -bool FSELevelFilter::ShouldLoad(const UActorComponent* Component) const +bool FSELevelFilter::Stores(const UActorComponent* Component) const { - return IsValid(Component) && LoadComponentFilter.IsClassAllowed(Component->GetClass()); + return ComponentFilter.IsAllowed(Component->GetClass()); } bool FSELevelFilter::StoresTransform(const UActorComponent* Component) diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index 74c1ffd..a710974 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -49,7 +49,7 @@ void ULifetimeComponent::EndPlay(EEndPlayReason::Type Reason) void ULifetimeComponent::OnSaveBegan(const FSELevelFilter& Filter) { - if (Filter.ShouldSave(GetOwner())) + if (Filter.Stores(GetOwner())) { Saved.Broadcast(); } @@ -57,7 +57,7 @@ void ULifetimeComponent::OnSaveBegan(const FSELevelFilter& Filter) void ULifetimeComponent::OnLoadFinished(const FSELevelFilter& Filter, bool bError) { - if (Filter.ShouldSave(GetOwner())) + if (Filter.Stores(GetOwner())) { Resume.Broadcast(); } diff --git a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp index 28d4d0e..f9a4347 100644 --- a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp @@ -4,4 +4,45 @@ ///////////////////////////////////////////////////// -// FSaveFileTask +// FLoadFileTask + +FLoadFileTask::FLoadFileTask(USaveManager* Manager, USaveSlot* InLastSlot, FStringView SlotName) + : Manager(Manager), SlotName(SlotName), LastSlot(InLastSlot) +{ + if (LastSlot.IsValid()) // If an slot was provided, that could be reused, mark it async + { + LastSlot->SetInternalFlags(EInternalObjectFlags::Async); + LastSlotData = LastSlot->GetData(); + if (LastSlotData.IsValid()) + { + LastSlotData->SetInternalFlags(EInternalObjectFlags::Async); + } + } +} + +FLoadFileTask::~FLoadFileTask() +{ + if (Slot.IsValid()) + { + Slot->ClearInternalFlags(EInternalObjectFlags::Async); + if (USaveSlotData* SlotData = Slot->GetData()) + { + SlotData->ClearInternalFlags(EInternalObjectFlags::Async); + } + } + if (LastSlot.IsValid()) + { + LastSlot->ClearInternalFlags(EInternalObjectFlags::Async); + } + if (LastSlotData.IsValid()) + { + LastSlotData->ClearInternalFlags(EInternalObjectFlags::Async); + } +} + +void FLoadFileTask::DoWork() +{ + USaveSlot* NewSlot = LastSlot.Get(); + FSaveFileHelpers::LoadFile(SlotName, NewSlot, true, Manager.Get()); + Slot = NewSlot; +} diff --git a/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp index 1304670..0b4802b 100644 --- a/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp +++ b/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp @@ -44,9 +44,9 @@ void FLoadSlotsTask::DoWork() LoadedSlots.Reserve(LoadedFiles.Num()); for (const auto& File : LoadedFiles) { - USaveSlot* Slot = nullptr; - File.CreateAndDeserializeSlot(Slot, Manager); - LoadedSlots.Add(Slot); + LoadedSlots.Add(Cast( + FSaveFileHelpers::DeserializeObject(nullptr, File.InfoClassName, Manager, File.InfoBytes) + )); } if (!bLoadingSingleInfo && bSortByRecent) diff --git a/Source/SaveExtension/Private/SaveFileHelpers.cpp b/Source/SaveExtension/Private/SaveFileHelpers.cpp index 7a458cf..df0ae3e 100644 --- a/Source/SaveExtension/Private/SaveFileHelpers.cpp +++ b/Source/SaveExtension/Private/SaveFileHelpers.cpp @@ -3,6 +3,7 @@ #include "SaveFileHelpers.h" #include "Multithreading/SaveFileTask.h" +#include "Serialization/SEArchive.h" #include "SaveSlot.h" #include "SaveSlotData.h" @@ -209,22 +210,6 @@ void FSaveFile::SerializeData(USaveSlotData* SlotData) SlotData->Serialize(Ar); } -void FSaveFile::CreateAndDeserializeSlot(USaveSlot*& Slot, const UObject* Outer) const -{ - TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeSlot); - UObject* Object = nullptr; - FSaveFileHelpers::DeserializeObject(Object, InfoClassName, Outer, InfoBytes); - Slot = Cast(Object); -} - -void FSaveFile::CreateAndDeserializeData(USaveSlot* Slot) const -{ - TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::CreateAndDeserializeData); - UObject* Object = nullptr; - FSaveFileHelpers::DeserializeObject(Object, DataClassName, Slot, DataBytes); - Slot->AssignData(Cast(Object)); -} - bool FSaveFileHelpers::SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression) { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFileHelpers::SaveFile); @@ -261,10 +246,17 @@ bool FSaveFileHelpers::LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLo { FSaveFile File{}; File.Read(Reader, !bLoadData); - File.CreateAndDeserializeSlot(Slot, Outer); + + { + TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeInfo) + Slot = Cast(DeserializeObject(Slot, File.InfoClassName, Outer, File.InfoBytes)); + } if (bLoadData) { - File.CreateAndDeserializeData(Slot); + TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeData) + Slot->AssignData(Cast( + DeserializeObject(Slot->GetData(), File.DataClassName, Slot, File.DataBytes)) + ); } return true; } @@ -297,12 +289,13 @@ FString FSaveFileHelpers::GetThumbnailPath(FStringView SlotName) return GetSaveFolder() / FString::Printf(TEXT("%s.png"), SlotName.GetData()); } -void FSaveFileHelpers::DeserializeObject( - UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes) +UObject* FSaveFileHelpers::DeserializeObject(UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes) { + UObject* Object = Hint; + if (ClassName.IsEmpty() || Bytes.Num() <= 0) { - return; + return Object; } UClass* ObjectClass = FindObject(nullptr, ClassName.GetData()); @@ -312,10 +305,11 @@ void FSaveFileHelpers::DeserializeObject( } if (!ObjectClass) { - return; + return Object; } - if (!Object) + // Can only reuse object if class matches + if (!Object || Object->GetClass() != ObjectClass) { if (!Outer) { @@ -324,16 +318,10 @@ void FSaveFileHelpers::DeserializeObject( Object = NewObject(const_cast(Outer), ObjectClass); } - // Can only reuse object if class matches - else if (Object->GetClass() != ObjectClass) - { - return; - } - if (Object) - { - FMemoryReader Reader{Bytes}; - FObjectAndNameAsStringProxyArchive Ar(Reader, true); - Object->Serialize(Ar); - } + check(Object); + FMemoryReader Reader{Bytes}; + FSEArchive Ar(Reader, true); + Object->Serialize(Ar); + return Object; } diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 1acd6d0..62bb91e 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -6,10 +6,10 @@ #include "Multithreading/LoadSlotsTask.h" #include "SaveFileHelpers.h" #include "SaveSettings.h" -#include "Serialization/SlotDataTask_LevelLoader.h" -#include "Serialization/SlotDataTask_LevelSaver.h" -#include "Serialization/SlotDataTask_Loader.h" -#include "Serialization/SlotDataTask_Saver.h" +#include "Serialization/SEDataTask_LoadLevel.h" +#include "Serialization/SEDataTask_SaveLevel.h" +#include "Serialization/SEDataTask_Load.h" +#include "Serialization/SEDataTask_Save.h" #include #include @@ -229,12 +229,12 @@ bool USaveManager::SaveSlot(FName SlotName, bool bOverrideIfNeeded, bool bScreen check(World); // Launch task, always fail if it didn't finish or wasn't scheduled - auto* Task = CreateTask() - ->Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) - ->Bind(OnSaved) - ->Start(); + auto& Task = CreateTask() + .Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) + .Bind(OnSaved) + .Start(); - return Task->IsSucceeded() || Task->IsScheduled(); + return Task.IsSucceeded() || Task.IsScheduled(); } bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) @@ -246,9 +246,8 @@ bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) AssureActiveSlot(); - auto* Task = CreateTask()->Setup(SlotName)->Bind(OnLoaded)->Start(); - - return Task->IsSucceeded() || Task->IsScheduled(); + auto& Task = CreateTask().Setup(SlotName).Bind(OnLoaded).Start(); + return Task.IsSucceeded() || Task.IsScheduled(); } bool USaveManager::DeleteSlot(FName SlotName) @@ -422,13 +421,13 @@ void USaveManager::SerializeStreamingLevel(ULevelStreaming* LevelStreaming) { if (!LevelStreaming->GetLoadedLevel()->bIsBeingRemoved) { - CreateTask()->Setup(LevelStreaming)->Start(); + CreateTask().Setup(LevelStreaming).Start(); } } void USaveManager::DeserializeStreamingLevel(ULevelStreaming* LevelStreaming) { - CreateTask()->Setup(LevelStreaming)->Start(); + CreateTask().Setup(LevelStreaming).Start(); } USaveSlot* USaveManager::LoadInfo(FName SlotName) @@ -450,17 +449,9 @@ USaveSlot* USaveManager::LoadInfo(FName SlotName) return Infos.Num() > 0 ? Infos[0] : nullptr; } -USaveSlotDataTask* USaveManager::CreateTask(TSubclassOf TaskType) -{ - USaveSlotDataTask* Task = NewObject(this, TaskType.Get()); - Task->Prepare(ActiveSlot); - Tasks.Add(Task); - return Task; -} - -void USaveManager::FinishTask(USaveSlotDataTask* Task) +void USaveManager::FinishTask(FSEDataTask* Task) { - Tasks.Remove(Task); + Tasks.RemoveAll([Task](auto& TaskPtr) { return TaskPtr.Get() == Task; }); // Start next task if (Tasks.Num() > 0) @@ -483,15 +474,14 @@ FName USaveManager::GetFileNameFromId(const int32 SlotId) const bool USaveManager::IsLoading() const { - return HasTasks() && - (Tasks[0]->IsA() || Tasks[0]->IsA()); + return HasTasks() && Tasks[0]->Type == ESETaskType::Load; } void USaveManager::Tick(float DeltaTime) { if (Tasks.Num()) { - USaveSlotDataTask* Task = Tasks[0]; + FSEDataTask* Task = Tasks[0].Get(); check(Task); if (Task->IsRunning()) { @@ -512,11 +502,12 @@ void USaveManager::UnsubscribeFromEvents(const TScriptInterfacetemplate Implements()); // C++ event @@ -525,14 +516,15 @@ void USaveManager::OnSaveBegan(const FSELevelFilter& Filter) Interface->OnSaveBegan(Filter); } ISaveExtensionInterface::Execute_ReceiveOnSaveBegan(Object, Filter); - }); + });*/ } -void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bError) +void USaveManager::OnSaveFinished(const bool bError) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnSaveFinished); - IterateSubscribedInterfaces([&Filter, bError](auto* Object) { + // TODO: Needs reworking + /*IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -541,7 +533,7 @@ void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bErro Interface->OnSaveFinished(Filter, bError); } ISaveExtensionInterface::Execute_ReceiveOnSaveFinished(Object, Filter, bError); - }); + });*/ if (!bError) { @@ -549,11 +541,11 @@ void USaveManager::OnSaveFinished(const FSELevelFilter& Filter, const bool bErro } } -void USaveManager::OnLoadBegan(const FSELevelFilter& Filter) +void USaveManager::OnLoadBegan() { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadBegan); - IterateSubscribedInterfaces([&Filter](auto* Object) { + /*IterateSubscribedInterfaces([&Filter](auto* Object) { check(Object->template Implements()); // C++ event @@ -562,14 +554,14 @@ void USaveManager::OnLoadBegan(const FSELevelFilter& Filter) Interface->OnLoadBegan(Filter); } ISaveExtensionInterface::Execute_ReceiveOnLoadBegan(Object, Filter); - }); + });*/ } -void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bError) +void USaveManager::OnLoadFinished(const bool bError) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadFinished); - IterateSubscribedInterfaces([&Filter, bError](auto* Object) { + /*IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -578,7 +570,7 @@ void USaveManager::OnLoadFinished(const FSELevelFilter& Filter, const bool bErro Interface->OnLoadFinished(Filter, bError); } ISaveExtensionInterface::Execute_ReceiveOnLoadFinished(Object, Filter, bError); - }); + });*/ if (!bError) { @@ -593,9 +585,9 @@ void USaveManager::OnMapLoadStarted(const FString& MapName) void USaveManager::OnMapLoadFinished(UWorld* LoadedWorld) { - if (auto* ActiveLoader = Cast(Tasks.Num() ? Tasks[0] : nullptr)) + if (IsLoading()) { - ActiveLoader->OnMapLoaded(); + static_cast(Tasks[0].Get())->OnMapLoaded(); } UpdateLevelStreamings(); diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 71bd96a..40f5d1d 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -2,6 +2,8 @@ #include "SaveSlot.h" +#include "SaveFileHelpers.h" + #include #include #include @@ -11,8 +13,9 @@ #include -USaveSlot::USaveSlot() +void USaveSlot::PostInitProperties() { + Super::PostInitProperties(); Data = NewObject(this, DataClass, TEXT("SlotData")); } @@ -126,29 +129,18 @@ int32 USaveSlot::GetIndex_Implementation() const return OnGetIndex(); } - -const FSEActorClassFilter& USaveSlot::GetActorFilter(bool bIsLoading) const -{ - return (bIsLoading && bUseLoadActorFilter) ? LoadActorFilter : ActorFilter; -} - -const FSEComponentClassFilter& USaveSlot::GetComponentFilter(bool bIsLoading) const -{ - return (bIsLoading && bUseLoadActorFilter) ? LoadComponentFilter : ComponentFilter; -} - bool USaveSlot::IsMTSerializationLoad() const { - return MultithreadedSerialization == ESaveASyncMode::LoadAsync || - MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedSerialization == ESEAsyncMode::LoadAsync || + MultithreadedSerialization == ESEAsyncMode::SaveAndLoadAsync; } bool USaveSlot::IsMTSerializationSave() const { - return MultithreadedSerialization == ESaveASyncMode::SaveAsync || - MultithreadedSerialization == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedSerialization == ESEAsyncMode::SaveAsync || + MultithreadedSerialization == ESEAsyncMode::SaveAndLoadAsync; } -ESaveASyncMode USaveSlot::GetFrameSplitSerialization() const +ESEAsyncMode USaveSlot::GetFrameSplitSerialization() const { return FrameSplittedSerialization; } @@ -159,29 +151,38 @@ float USaveSlot::GetMaxFrameMs() const bool USaveSlot::IsFrameSplitLoad() const { - return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESaveASyncMode::LoadAsync || - FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); + return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESEAsyncMode::LoadAsync || + FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsFrameSplitSave() const { - return !IsMTSerializationSave() && (FrameSplittedSerialization == ESaveASyncMode::SaveAsync || - FrameSplittedSerialization == ESaveASyncMode::SaveAndLoadAsync); + return !IsMTSerializationSave() && (FrameSplittedSerialization == ESEAsyncMode::SaveAsync || + FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsMTFilesLoad() const { - return MultithreadedFiles == ESaveASyncMode::LoadAsync || - MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedFiles == ESEAsyncMode::LoadAsync || + MultithreadedFiles == ESEAsyncMode::SaveAndLoadAsync; } bool USaveSlot::IsMTFilesSave() const { - return MultithreadedFiles == ESaveASyncMode::SaveAsync || - MultithreadedFiles == ESaveASyncMode::SaveAndLoadAsync; + return MultithreadedFiles == ESEAsyncMode::SaveAsync || + MultithreadedFiles == ESEAsyncMode::SaveAndLoadAsync; +} + +bool USaveSlot::IsLoadingOrSaving() const +{ + return HasAnyInternalFlags(EInternalObjectFlags::Async); +} + +void USaveSlot::GetLevelFilter_Implementation(bool bIsLoading, FSELevelFilter& OutFilter) const +{ + OnGetLevelFilter(bIsLoading, OutFilter); } -FSELevelFilter USaveSlot::ToFilter() const +void USaveSlot::OnGetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) const { - FSELevelFilter Filter{}; - Filter.FromSlot(*this); - return Filter; + OutFilter.ActorFilter = ActorFilter; + OutFilter.ComponentFilter = ComponentFilter; } diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index c3b7786..0a54930 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -11,13 +11,9 @@ void USaveSlotData::Serialize(FArchive& Ar) { Super::Serialize(Ar); - - Ar << bStoreGameInstance; + Ar << TimeSeconds; Ar << GameInstance; - - static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; - LevelFilterType->SerializeItem(Ar, &GlobalLevelFilter, nullptr); - MainLevel.Serialize(Ar); + RootLevel.Serialize(Ar); Ar << SubLevels; } @@ -26,7 +22,7 @@ void USaveSlotData::CleanRecords(bool bKeepSublevels) // Clean Up serialization data GameInstance = {}; - MainLevel.CleanRecords(); + RootLevel.CleanRecords(); if (!bKeepSublevels) { SubLevels.Empty(); diff --git a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp index da85dfa..eb9ac3a 100644 --- a/Source/SaveExtension/Private/Serialization/LevelRecords.cpp +++ b/Source/SaveExtension/Private/Serialization/LevelRecords.cpp @@ -15,13 +15,6 @@ bool FLevelRecord::Serialize(FArchive& Ar) { Super::Serialize(Ar); - Ar << bOverrideGlobalFilter; - if (bOverrideGlobalFilter) - { - static UScriptStruct* const LevelFilterType{FSELevelFilter::StaticStruct()}; - LevelFilterType->SerializeItem(Ar, &Filter, nullptr); - } - Ar << LevelScript; Ar << Actors; diff --git a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp index 16e4bac..f8a91f9 100644 --- a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp +++ b/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp @@ -21,15 +21,21 @@ void FMTTask_SerializeActors::DoWork() SerializeGameInstance(); } + TArray ActorsToSerialize; for (int32 I = 0; I < Num; ++I) { const AActor* const Actor = (*LevelActors)[StartIndex + I]; - if (Actor && Filter.ShouldSave(Actor)) + if (Actor && Filter->Stores(Actor)) { - FActorRecord& Record = ActorRecords.AddDefaulted_GetRef(); - SerializeActor(Actor, Record); + ActorsToSerialize.Add(Actor); } } + + for (const AActor* Actor : ActorsToSerialize) + { + FActorRecord& Record = ActorRecords.AddDefaulted_GetRef(); + SerializeActor(Actor, Record); + } } void FMTTask_SerializeActors::SerializeGameInstance() @@ -56,9 +62,9 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& Record = {Actor}; Record.bHiddenInGame = Actor->IsHidden(); - Record.bIsProcedural = Filter.IsProcedural(Actor); + Record.bIsProcedural = Filter->IsProcedural(Actor); - if (Filter.StoresTags(Actor)) + if (Filter->StoresTags(Actor)) { Record.Tags = Actor->Tags; } @@ -67,18 +73,18 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& // Only save save-tags for (const auto& Tag : Actor->Tags) { - if (Filter.IsSaveTag(Tag)) + if (Filter->IsSaveTag(Tag)) { Record.Tags.Add(Tag); } } } - if (Filter.StoresTransform(Actor)) + if (Filter->StoresTransform(Actor)) { Record.Transform = Actor->GetTransform(); - if (Filter.StoresPhysics(Actor)) + if (Filter->StoresPhysics(Actor)) { USceneComponent* const Root = Actor->GetRootComponent(); if (Root && Root->Mobility == EComponentMobility::Movable) @@ -96,10 +102,7 @@ bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& } } - if (Filter.bStoreComponents) - { - SerializeActorComponents(Actor, Record, 1); - } + SerializeActorComponents(Actor, Record, 1); TRACE_CPUPROFILER_EVENT_SCOPE(Serialize); FMemoryWriter MemoryWriter(Record.Data, true); @@ -114,17 +117,22 @@ void FMTTask_SerializeActors::SerializeActorComponents( { TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents); + if (!Filter->StoresAnyComponents()) + { + return; + } + const TSet& Components = Actor->GetComponents(); for (auto* Component : Components) { TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActorComponents | Component); - if (Filter.ShouldSave(Component)) + if (IsValid(Component) && Filter->Stores(Component)) { FComponentRecord ComponentRecord; ComponentRecord.Name = Component->GetFName(); ComponentRecord.Class = Component->GetClass(); - if (Filter.StoresTransform(Component)) + if (Filter->StoresTransform(Component)) { const USceneComponent* Scene = CastChecked(Component); if (Scene->Mobility == EComponentMobility::Movable) @@ -133,7 +141,7 @@ void FMTTask_SerializeActors::SerializeActorComponents( } } - if (Filter.StoresTags(Component)) + if (Filter->StoresTags(Component)) { ComponentRecord.Tags = Component->ComponentTags; } diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp new file mode 100644 index 0000000..fe47f1a --- /dev/null +++ b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp @@ -0,0 +1,52 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Serialization/SEDataTask.h" + +#include "SaveManager.h" + + +///////////////////////////////////////////////////// +// USaveDataTask + +FSEDataTask& FSEDataTask::Start() +{ + // If not running and first task is this + if (!bRunning && Manager->Tasks.Num() > 0 && Manager->Tasks[0].Get() == this) + { + bRunning = true; + OnStart(); + } + return *this; +} + +void FSEDataTask::Finish(bool bSuccess) +{ + if (bRunning) + { + OnFinish(bSuccess); + Manager->FinishTask(this); + bFinished = true; + bSucceeded = bSuccess; + } +} + +bool FSEDataTask::IsScheduled() const +{ + return Manager->Tasks.ContainsByPredicate([this](auto& Task) { + return Task.Get() == this; + }); +} + +FLevelRecord* FSEDataTask::FindLevelRecord(const ULevelStreaming* Level) const +{ + if (Level) + { + return SlotData->SubLevels.FindByKey(Level); + } + return &SlotData->RootLevel; +} + +UWorld* FSEDataTask::GetWorld() const +{ + return Manager->GetWorld(); +} diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp similarity index 55% rename from Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp rename to Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 6cde5f2..7ff3ec2 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Loader.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/SlotDataTask_Loader.h" +#include "Serialization/SEDataTask_Load.h" #include "Misc/SlotHelpers.h" #include "SaveManager.h" @@ -14,38 +14,20 @@ ///////////////////////////////////////////////////// -// Helpers +// USaveDataTask_Loader -namespace Loader +FSEDataTask_Load::~FSEDataTask_Load() { - static int32 RemoveSingleRecordPtrSwap( - TArray& Records, AActor* Actor, bool bAllowShrinking = true) + if (LoadDataTask) { - if (!Actor) - { - return 0; - } - - const int32 I = Records.IndexOfByPredicate([Records, Actor](auto* Record) { - return *Record == Actor; - }); - if (I != INDEX_NONE) - { - Records.RemoveAtSwap(I, 1, bAllowShrinking); - return 1; - } - return 0; + LoadDataTask->EnsureCompletion(false); + delete LoadDataTask; } -} // namespace Loader - - -///////////////////////////////////////////////////// -// USaveDataTask_Loader +} -void USaveSlotDataTask_Loader::OnStart() +void FSEDataTask_Load::OnStart() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnStart); - USaveManager* Manager = GetManager(); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::OnStart); Slot = Manager->LoadInfo(SlotName); SELog(Slot, "Loading from Slot " + SlotName.ToString()); @@ -78,7 +60,7 @@ void USaveSlotDataTask_Loader::OnStart() return; } - UGameplayStatics::OpenLevel(this, FName{MapToOpen}); + UGameplayStatics::OpenLevel(Manager, FName{MapToOpen}); SELog(Slot, "Slot '" + SlotName.ToString() + "' is recorded on another Map. Loading before charging slot.", @@ -95,9 +77,9 @@ void USaveSlotDataTask_Loader::OnStart() } } -void USaveSlotDataTask_Loader::Tick(float DeltaTime) +void FSEDataTask_Load::Tick(float DeltaTime) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::Tick); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::Tick); switch (LoadState) { case ELoadDataTaskState::Deserializing: @@ -115,9 +97,9 @@ void USaveSlotDataTask_Loader::Tick(float DeltaTime) } } -void USaveSlotDataTask_Loader::OnFinish(bool bSuccess) +void FSEDataTask_Load::OnFinish(bool bSuccess) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::OnFinish); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::OnFinish); if (bSuccess) { SELog(Slot, "Finished Loading", FColor::Green); @@ -126,21 +108,10 @@ void USaveSlotDataTask_Loader::OnFinish(bool bSuccess) // Execute delegates Delegate.ExecuteIfBound((bSuccess) ? Slot : nullptr); - GetManager()->OnLoadFinished(SlotData ? GetGlobalFilter() : FSELevelFilter{}, !bSuccess); -} - -void USaveSlotDataTask_Loader::BeginDestroy() -{ - if (LoadDataTask) - { - LoadDataTask->EnsureCompletion(false); - delete LoadDataTask; - } - - Super::BeginDestroy(); + Manager->OnLoadFinished(!bSuccess); } -void USaveSlotDataTask_Loader::OnMapLoaded() +void FSEDataTask_Load::OnMapLoaded() { if (LoadState != ELoadDataTaskState::LoadingMap) { @@ -167,9 +138,9 @@ void USaveSlotDataTask_Loader::OnMapLoaded() } } -void USaveSlotDataTask_Loader::StartDeserialization() +void FSEDataTask_Load::StartDeserialization() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::StartDeserialization); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::StartDeserialization); check(Slot); LoadState = ELoadDataTaskState::Deserializing; @@ -184,11 +155,10 @@ void USaveSlotDataTask_Loader::StartDeserialization() Slot->Stats.LoadDate = FDateTime::Now(); - GetManager()->OnLoadBegan(GetGlobalFilter()); // Apply current Info if succeeded - GetManager()->AssignActiveSlot(Slot); + Manager->AssignActiveSlot(Slot); - BakeAllFilters(); + Manager->OnLoadBegan(); BeforeDeserialize(); @@ -198,9 +168,9 @@ void USaveSlotDataTask_Loader::StartDeserialization() DeserializeSync(); } -void USaveSlotDataTask_Loader::StartLoadingData() +void FSEDataTask_Load::StartLoadingData() { - LoadDataTask = new FAsyncTask(GetManager(), SlotName.ToString()); + LoadDataTask = new FAsyncTask(Manager, Slot, SlotName.ToString()); if (Slot->IsMTFilesLoad()) LoadDataTask->StartBackgroundTask(); @@ -208,7 +178,7 @@ void USaveSlotDataTask_Loader::StartLoadingData() LoadDataTask->StartSynchronousTask(); } -USaveSlotData* USaveSlotDataTask_Loader::GetLoadedData() const +USaveSlotData* FSEDataTask_Load::GetLoadedData() const { if (IsDataLoaded()) { @@ -217,23 +187,23 @@ USaveSlotData* USaveSlotDataTask_Loader::GetLoadedData() const return nullptr; } -void USaveSlotDataTask_Loader::BeforeDeserialize() +void FSEDataTask_Load::BeforeDeserialize() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::BeforeDeserialize); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::BeforeDeserialize); UWorld* World = GetWorld(); // Set current game time to the saved value World->TimeSeconds = SlotData->TimeSeconds; - if (SlotData->bStoreGameInstance) + if (Slot->bStoreGameInstance) { DeserializeGameInstance(); } } -void USaveSlotDataTask_Loader::DeserializeSync() +void FSEDataTask_Load::DeserializeSync() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeSync); const UWorld* World = GetWorld(); check(World); @@ -259,34 +229,31 @@ void USaveSlotDataTask_Loader::DeserializeSync() FinishedDeserializing(); } -void USaveSlotDataTask_Loader::DeserializeLevelSync( +void FSEDataTask_Load::DeserializeLevelSync( const ULevel* Level, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeLevelSync); if (!IsValid(Level)) + { return; + } const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); - if (FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel)) + const FLevelRecord& LevelRecord = *FindLevelRecord(StreamingLevel); + for(const auto& RecordToActor : LevelRecord.RecordsToActors) { - const auto& Filter = GetLevelFilter(*LevelRecord); - - for (auto ActorItr = Level->Actors.CreateConstIterator(); ActorItr; ++ActorItr) - { - auto* Actor = *ActorItr; - if (IsValid(Actor) && Filter.ShouldSave(Actor)) - { - DeserializeLevel_Actor(Actor, *LevelRecord, Filter); - } - } + const FActorRecord* Record = RecordToActor.Key; + AActor* Actor = RecordToActor.Value.Get(); + check(Record && Actor); + DeserializeActor(Actor, *Record, LevelRecord); } } -void USaveSlotDataTask_Loader::DeserializeASync() +void FSEDataTask_Load::DeserializeASync() { // Deserialize world { @@ -297,7 +264,7 @@ void USaveSlotDataTask_Loader::DeserializeASync() } } -void USaveSlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStreaming* StreamingLevel) +void FSEDataTask_Load::DeserializeLevelASync(ULevel* Level, ULevelStreaming* StreamingLevel) { check(IsValid(Level)); @@ -318,48 +285,37 @@ void USaveSlotDataTask_Loader::DeserializeLevelASync(ULevel* Level, ULevelStream CurrentSLevel = StreamingLevel; CurrentActorIndex = 0; - // Copy actors array. New actors won't be considered for deserialization - CurrentLevelActors.Empty(Level->Actors.Num()); - for (auto* Actor : Level->Actors) - { - if (IsValid(Actor)) - { - CurrentLevelActors.Add(Actor); - } - } - DeserializeASyncLoop(StartMS); } -void USaveSlotDataTask_Loader::DeserializeASyncLoop(float StartMS) +void FSEDataTask_Load::DeserializeASyncLoop(float StartMS) { - FLevelRecord* LevelRecord = FindLevelRecord(CurrentSLevel.Get()); - if (!LevelRecord) - { - return; - } - - const auto& Filter = GetLevelFilter(*LevelRecord); - if (StartMS <= 0) { StartMS = GetTimeMilliseconds(); } + FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); + // Continue Iterating actors every tick - for (; CurrentActorIndex < CurrentLevelActors.Num(); ++CurrentActorIndex) + for (; CurrentActorIndex < LevelRecord.RecordsToActors.Num(); ++CurrentActorIndex) { - AActor* const Actor{CurrentLevelActors[CurrentActorIndex].Get()}; - if (IsValid(Actor) && Filter.ShouldSave(Actor)) + auto& RecordToActor = LevelRecord.RecordsToActors[CurrentActorIndex]; + + const FActorRecord* Record = RecordToActor.Key; + AActor* Actor = RecordToActor.Value.Get(); + check(Record); + if (!Actor) { - DeserializeLevel_Actor(Actor, *LevelRecord, Filter); + continue; + } + DeserializeActor(Actor, *Record, LevelRecord); - const float CurrentMS = GetTimeMilliseconds(); + const float CurrentMS = GetTimeMilliseconds(); + if (CurrentMS - StartMS >= MaxFrameMs) + { // If x milliseconds passed, stop and continue on next frame - if (CurrentMS - StartMS >= MaxFrameMs) - { - return; - } + return; } } @@ -380,49 +336,66 @@ void USaveSlotDataTask_Loader::DeserializeASyncLoop(float StartMS) FinishedDeserializing(); } -void USaveSlotDataTask_Loader::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) +void FSEDataTask_Load::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::PrepareLevel); - - const auto& Filter = GetLevelFilter(LevelRecord); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::PrepareLevel); + Slot->GetLevelFilter(true, LevelRecord.Filter); + LevelRecord.Filter.BakeAllowedClasses(); // Records not contained in Scene Actors => Actors to be Respawned // Scene Actors not contained in loaded records => Actors to be Destroyed // The rest => Just deserialize - TArray ActorsToSpawn; - ActorsToSpawn.Reserve(LevelRecord.Actors.Num()); + TArray ActorRecordsToSpawn; + ActorRecordsToSpawn.Reserve(LevelRecord.Actors.Num()); for (FActorRecord& Record : LevelRecord.Actors) { - ActorsToSpawn.Add(&Record); + ActorRecordsToSpawn.Add(&Record); } TArray ActorsToDestroy{}; - { - // O(M*Log(N)) + { // Filter actors by whether they should be destroyed or spawned - O(M*Log(N)) for (AActor* const Actor : Level->Actors) { - // Remove records which actors do exist - const bool bFoundActorRecord = Loader::RemoveSingleRecordPtrSwap(ActorsToSpawn, Actor, false) > 0; + if (UNLIKELY(!Actor)) + { + continue; + } - if (Actor && Filter.ShouldSave(Actor)) + const int32 Index = ActorRecordsToSpawn.IndexOfByPredicate([Actor](auto* Record) { + return *Record == Actor; + }); + if (Index != INDEX_NONE) // Actor found, therefore doesn't need to be spawned { - if (!bFoundActorRecord) // Don't destroy level actors + if (LevelRecord.Filter.Stores(Actor)) { - // If the actor wasn't found, mark it for destruction - Actor->Destroy(); + FActorRecord* Record = ActorRecordsToSpawn[Index]; + LevelRecord.RecordsToActors.Add({Record, Actor}); } + ActorRecordsToSpawn.RemoveAtSwap(Index, 1, false); + } + else if (LevelRecord.Filter.Stores(Actor)) + { + ActorsToDestroy.Add(Actor); } + // TODO: Consider unmatching class actors to be respawned } - ActorsToSpawn.Shrink(); + + } + + // The serializable actors that were not found will be destroyed + for (AActor* Actor : ActorsToDestroy) + { + Actor->Destroy(); } - // Create Actors that doesn't exist now but were saved - RespawnActors(ActorsToSpawn, Level); + // Spawn Actors that don't exist but were saved + ActorRecordsToSpawn.Shrink(); + RespawnActors(ActorRecordsToSpawn, Level, LevelRecord); } -void USaveSlotDataTask_Loader::FinishedDeserializing() +void FSEDataTask_Load::FinishedDeserializing() { // Clean serialization data SlotData->CleanRecords(true); @@ -430,15 +403,15 @@ void USaveSlotDataTask_Loader::FinishedDeserializing() Finish(true); } -void USaveSlotDataTask_Loader::PrepareAllLevels() +void FSEDataTask_Load::PrepareAllLevels() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::PrepareAllLevels); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::PrepareAllLevels); const UWorld* World = GetWorld(); check(World); - // Prepare Main level - PrepareLevel(World->GetCurrentLevel(), SlotData->MainLevel); + // Prepare root level + PrepareLevel(World->GetCurrentLevel(), SlotData->RootLevel); // Prepare other loaded sub-levels const TArray& Levels = World->GetStreamingLevels(); @@ -455,9 +428,9 @@ void USaveSlotDataTask_Loader::PrepareAllLevels() } } -void USaveSlotDataTask_Loader::RespawnActors(const TArray& Records, const ULevel* Level) +void FSEDataTask_Load::RespawnActors(const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::RespawnActors); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::RespawnActors); FActorSpawnParameters SpawnInfo{}; SpawnInfo.OverrideLevel = const_cast(Level); @@ -473,23 +446,11 @@ void USaveSlotDataTask_Loader::RespawnActors(const TArray& Record // We update the name on the record in case it changed Record->Name = NewActor->GetFName(); + LevelRecord.RecordsToActors.Add({Record, NewActor}); } } -void USaveSlotDataTask_Loader::DeserializeLevel_Actor( - AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter) -{ - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeLevel_Actor); - - // Find the record - const FActorRecord* const Record = LevelRecord.Actors.FindByKey(Actor); - if (Record && Record->IsValid() && Record->Class == Actor->GetClass()) - { - DeserializeActor(Actor, *Record, Filter); - } -} - -void USaveSlotDataTask_Loader::DeserializeGameInstance() +void FSEDataTask_Load::DeserializeGameInstance() { bool bSuccess = true; auto* GameInstance = GetWorld()->GetGameInstance(); @@ -509,41 +470,46 @@ void USaveSlotDataTask_Loader::DeserializeGameInstance() SELog(Slot, "Game Instance '" + Record.Name.ToString() + "'", FColor::Green, !bSuccess, 1); } -bool USaveSlotDataTask_Loader::DeserializeActor( - AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter) +bool FSEDataTask_Load::DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Loader::DeserializeActor); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeActor); + + if (Actor->GetClass() != ActorRecord.Class) + { + SELog(Slot, "Actor '" + ActorRecord.Name.ToString() + "' already exists but class doesn't match", FColor::Green, true, 1); + return false; + } // Always load saved tags - Actor->Tags = Record.Tags; + Actor->Tags = ActorRecord.Tags; const bool bSavesPhysics = FSELevelFilter::StoresPhysics(Actor); if (FSELevelFilter::StoresTransform(Actor)) { - Actor->SetActorTransform(Record.Transform); + Actor->SetActorTransform(ActorRecord.Transform); if (FSELevelFilter::StoresPhysics(Actor)) { USceneComponent* Root = Actor->GetRootComponent(); if (auto* Primitive = Cast(Root)) { - Primitive->SetPhysicsLinearVelocity(Record.LinearVelocity); - Primitive->SetPhysicsAngularVelocityInRadians(Record.AngularVelocity); + Primitive->SetPhysicsLinearVelocity(ActorRecord.LinearVelocity); + Primitive->SetPhysicsAngularVelocityInRadians(ActorRecord.AngularVelocity); } else { - Root->ComponentVelocity = Record.LinearVelocity; + Root->ComponentVelocity = ActorRecord.LinearVelocity; } } } - Actor->SetActorHiddenInGame(Record.bHiddenInGame); + Actor->SetActorHiddenInGame(ActorRecord.bHiddenInGame); - DeserializeActorComponents(Actor, Record, Filter, 2); + DeserializeActorComponents(Actor, ActorRecord, LevelRecord, 2); { // Serialize from Record Data - FMemoryReader MemoryReader(Record.Data, true); + FMemoryReader MemoryReader(ActorRecord.Data, true); FSEArchive Archive(MemoryReader, false); Actor->Serialize(Archive); } @@ -551,55 +517,55 @@ bool USaveSlotDataTask_Loader::DeserializeActor( return true; } -void USaveSlotDataTask_Loader::DeserializeActorComponents( - AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 Indent) +void FSEDataTask_Load::DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 Indent) { - if (Filter.bStoreComponents) + TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents); + + if (!LevelRecord.Filter.StoresAnyComponents()) { - TRACE_CPUPROFILER_EVENT_SCOPE(UUSaveSlotDataTask_Loader::DeserializeActorComponents); + return; + } - const TSet& Components = Actor->GetComponents(); - for (auto* Component : Components) + for (auto* Component : Actor->GetComponents()) + { + if (!IsValid(Component) || !LevelRecord.Filter.Stores(Component)) { - if (!Filter.ShouldSave(Component)) - { - continue; - } + continue; + } - // Find the record - const FComponentRecord* Record = ActorRecord.ComponentRecords.FindByKey(Component); - if (!Record) - { - SELog(Slot, "Component '" + Component->GetFName().ToString() + "' - Record not found", - FColor::Red, false, Indent + 1); - continue; - } + // Find the record + const FComponentRecord* Record = ActorRecord.ComponentRecords.FindByKey(Component); + if (!Record) + { + SELog(Slot, "Component '" + Component->GetFName().ToString() + "' - Record not found", + FColor::Red, false, Indent + 1); + continue; + } - if (FSELevelFilter::StoresTransform(Component)) + if (FSELevelFilter::StoresTransform(Component)) + { + USceneComponent* Scene = CastChecked(Component); + if (Scene->Mobility == EComponentMobility::Movable) { - USceneComponent* Scene = CastChecked(Component); - if (Scene->Mobility == EComponentMobility::Movable) - { - Scene->SetRelativeTransform(Record->Transform); - } + Scene->SetRelativeTransform(Record->Transform); } + } - if (FSELevelFilter::StoresTags(Component)) - { - Component->ComponentTags = Record->Tags; - } + if (FSELevelFilter::StoresTags(Component)) + { + Component->ComponentTags = Record->Tags; + } - if (!Component->GetClass()->IsChildOf()) - { - FMemoryReader MemoryReader(Record->Data, true); - FSEArchive Archive(MemoryReader, false); - Component->Serialize(Archive); - } + if (!Component->GetClass()->IsChildOf()) + { + FMemoryReader MemoryReader(Record->Data, true); + FSEArchive Archive(MemoryReader, false); + Component->Serialize(Archive); } } } -void USaveSlotDataTask_Loader::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const +void FSEDataTask_Load::FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const { OutLevelStreaming = nullptr; diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp new file mode 100644 index 0000000..cfcd469 --- /dev/null +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp @@ -0,0 +1,72 @@ +// Copyright 2015-2024 Piperift. All Rights Reserved. + +#include "Serialization/SEDataTask_LoadLevel.h" + + +///////////////////////////////////////////////////// +// USaveDataTask_LevelLoader + +void FSEDataTask_LoadLevel::OnStart() +{ + if (!SlotData || !StreamingLevel || !StreamingLevel->IsLevelLoaded()) + { + Finish(false); + return; + } + + FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); + if (!LevelRecord) + { + Finish(false); + return; + } + + PrepareLevel(StreamingLevel->GetLoadedLevel(), *LevelRecord); + + if (Slot->IsFrameSplitLoad()) + { + DeserializeLevelASync(StreamingLevel->GetLoadedLevel(), StreamingLevel); + } + else + { + DeserializeLevelSync(StreamingLevel->GetLoadedLevel(), StreamingLevel); + FinishedDeserializing(); + return; + } +} + +void FSEDataTask_LoadLevel::DeserializeASyncLoop(float StartMS /*= 0.0f*/) +{ + + if (StartMS <= 0) + { + StartMS = GetTimeMilliseconds(); + } + + FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); + + // Continue Iterating actors every tick + for (; CurrentActorIndex < LevelRecord.RecordsToActors.Num(); ++CurrentActorIndex) + { + auto& RecordToActor = LevelRecord.RecordsToActors[CurrentActorIndex]; + + const FActorRecord* Record = RecordToActor.Key; + AActor* Actor = RecordToActor.Value.Get(); + check(Record); + if (!Actor) + { + continue; + } + DeserializeActor(Actor, *Record, LevelRecord); + + const float CurrentMS = GetTimeMilliseconds(); + if (CurrentMS - StartMS >= MaxFrameMs) + { + // If x milliseconds passed, stop and continue on next frame + return; + } + } + + // All levels deserialized + FinishedDeserializing(); +} diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp similarity index 74% rename from Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp rename to Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 971d1ab..a4b9cc5 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_Saver.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/SlotDataTask_Saver.h" +#include "Serialization/SEDataTask_Save.h" #include "Misc/SlotHelpers.h" #include "SaveFileHelpers.h" @@ -13,12 +13,20 @@ ///////////////////////////////////////////////////// -// USaveDataTask_Saver +// FSEDataTask_Save -void USaveSlotDataTask_Saver::OnStart() +FSEDataTask_Save::~FSEDataTask_Save() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnStart); - USaveManager* Manager = GetManager(); + if (SaveTask) + { + SaveTask->EnsureCompletion(false); + delete SaveTask; + } +} + +void FSEDataTask_Save::OnStart() +{ + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::OnStart); Manager->AssureActiveSlot(); bool bSave = true; @@ -46,10 +54,11 @@ void USaveSlotDataTask_Saver::OnStart() { const UWorld* World = GetWorld(); - GetManager()->OnSaveBegan(GetGlobalFilter()); + Manager->OnSaveBegan(); Slot = Manager->GetActiveSlot(); SlotData = Slot->GetData(); + check(SlotData->GetClass() == Slot->DataClass); SlotData->CleanRecords(true); check(Slot && SlotData); @@ -87,12 +96,8 @@ void USaveSlotDataTask_Saver::OnStart() SlotData->TimeSeconds = World->TimeSeconds; } - // Save Level info in both files + // Save Level info Slot->Map = FName{FSlotHelpers::GetWorldName(World)}; - SlotData->Map = SlotData->Map; - - SlotData->bStoreGameInstance = Slot->bStoreGameInstance; - SlotData->GlobalLevelFilter = Slot->ToFilter(); SerializeWorld(); SaveFile(); @@ -101,10 +106,10 @@ void USaveSlotDataTask_Saver::OnStart() Finish(false); } -void USaveSlotDataTask_Saver::Tick(float DeltaTime) +void FSEDataTask_Save::Tick(float DeltaTime) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::Tick); - Super::Tick(DeltaTime); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::Tick); + FSEDataTask::Tick(DeltaTime); if (SaveTask && SaveTask->IsDone()) { @@ -122,9 +127,9 @@ void USaveSlotDataTask_Saver::Tick(float DeltaTime) } } -void USaveSlotDataTask_Saver::OnFinish(bool bSuccess) +void FSEDataTask_Save::OnFinish(bool bSuccess) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::OnFinish); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::OnFinish); if (bSuccess) { // Clean serialization data @@ -134,34 +139,22 @@ void USaveSlotDataTask_Saver::OnFinish(bool bSuccess) } // Execute delegates - USaveManager* Manager = GetManager(); - check(Manager); Delegate.ExecuteIfBound((Manager && bSuccess) ? Manager->GetActiveSlot() : nullptr); - Manager->OnSaveFinished(SlotData ? GetGlobalFilter() : FSELevelFilter{}, !bSuccess); -} - -void USaveSlotDataTask_Saver::BeginDestroy() -{ - if (SaveTask) - { - SaveTask->EnsureCompletion(false); - delete SaveTask; - } - Super::BeginDestroy(); + Manager->OnSaveFinished(!bSuccess); } -void USaveSlotDataTask_Saver::SerializeWorld() +void FSEDataTask_Save::SerializeWorld() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeWorld); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeWorld); // Must have Authority - if (!GetWorld()->GetAuthGameMode()) + const UWorld* World = GetWorld(); + if (!World->GetAuthGameMode()) { return; } - const UWorld* World = GetWorld(); SELog(Slot, "World '" + World->GetName() + "'", FColor::Green, false, 1); const TArray& Levels = World->GetStreamingLevels(); @@ -184,24 +177,32 @@ void USaveSlotDataTask_Saver::SerializeWorld() RunScheduledTasks(); } -void USaveSlotDataTask_Saver::PrepareAllLevels(const TArray& Levels) +void FSEDataTask_Save::PrepareAllLevels(const TArray& Levels) { - BakeAllFilters(); + // Prepare root level + PrepareLevel(GetWorld()->GetCurrentLevel(), SlotData->RootLevel); // Create the sub-level records if non existent for (const ULevelStreaming* Level : Levels) { if (Level->IsLevelLoaded()) { - SlotData->SubLevels.AddUnique({*Level}); + FLevelRecord& LevelRecord = SlotData->SubLevels.Add_GetRef({*Level}); + PrepareLevel(Level->GetLoadedLevel(), LevelRecord); } } } -void USaveSlotDataTask_Saver::SerializeLevelSync( +void FSEDataTask_Save::PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord) +{ + Slot->GetLevelFilter(true, LevelRecord.Filter); + LevelRecord.Filter.BakeAllowedClasses(); +} + +void FSEDataTask_Save::SerializeLevelSync( const ULevel* Level, int32 AssignedTasks, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SerializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeLevelSync); check(IsValid(Level)); if (!Slot->IsMTSerializationSave()) @@ -214,7 +215,7 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level - FLevelRecord* LevelRecord = &SlotData->MainLevel; + FLevelRecord* LevelRecord = &SlotData->RootLevel; if (StreamingLevel) { LevelRecord = FindLevelRecord(StreamingLevel); @@ -224,8 +225,6 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( // Empty level record before serializing it LevelRecord->CleanRecords(); - auto& Filter = GetLevelFilter(*LevelRecord); - const int32 MinObjectsPerTask = 40; const int32 ActorCount = Level->Actors.Num(); const int32 NumBalancedPerTask = FMath::CeilToInt((float) ActorCount / AssignedTasks); @@ -239,18 +238,18 @@ void USaveSlotDataTask_Saver::SerializeLevelSync( const int32 NumToSerialize = FMath::Min(NumRemaining, NumPerTask); // First task saves the GameInstance - bool bStoreGameInstance = Index <= 0 && SlotData->bStoreGameInstance; + bool bStoreGameInstance = Index <= 0 && Slot->bStoreGameInstance; // Add new Task Tasks.Emplace(FMTTask_SerializeActors{GetWorld(), SlotData, &Level->Actors, Index, NumToSerialize, - bStoreGameInstance, LevelRecord, Filter}); + bStoreGameInstance, LevelRecord, &LevelRecord->Filter}); Index += NumToSerialize; } } -void USaveSlotDataTask_Saver::RunScheduledTasks() +void FSEDataTask_Save::RunScheduledTasks() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::RunScheduledTasks); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::RunScheduledTasks); // Start all serialization tasks if (Tasks.Num() > 0) { @@ -277,11 +276,9 @@ void USaveSlotDataTask_Saver::RunScheduledTasks() Tasks.Empty(); } -void USaveSlotDataTask_Saver::SaveFile() +void FSEDataTask_Save::SaveFile() { - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask_Saver::SaveFile); - USaveManager* Manager = GetManager(); - + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SaveFile); SaveTask = new FAsyncTask(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp similarity index 79% rename from Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp rename to Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp index cd1bda8..a45e9c5 100644 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelSaver.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp @@ -1,12 +1,12 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/SlotDataTask_LevelSaver.h" +#include "Serialization/SEDataTask_SaveLevel.h" ///////////////////////////////////////////////////// // FSaveDataTask_LevelSaver -void USaveSlotDataTask_LevelSaver::OnStart() +void FSEDataTask_SaveLevel::OnStart() { if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) { @@ -17,7 +17,7 @@ void USaveSlotDataTask_LevelSaver::OnStart() return; } - GetLevelFilter(*LevelRecord).BakeAllowedClasses(); + PrepareLevel(StreamingLevel->GetLoadedLevel(), *LevelRecord); const int32 NumberOfThreads = FMath::Max(1, FPlatformMisc::NumberOfWorkerThreadsToSpawn()); SerializeLevelSync(StreamingLevel->GetLoadedLevel(), NumberOfThreads, StreamingLevel); diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp deleted file mode 100644 index b25eb4c..0000000 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Serialization/SlotDataTask.h" - -#include "SaveManager.h" - - -///////////////////////////////////////////////////// -// USaveDataTask - -USaveSlotDataTask* USaveSlotDataTask::Start() -{ - const USaveManager* Manager = GetManager(); - - // If not running and first task is this - if (!bRunning && Manager->Tasks.Num() > 0 && Manager->Tasks[0] == this) - { - bRunning = true; - OnStart(); - } - return this; -} - -void USaveSlotDataTask::Finish(bool bSuccess) -{ - if (bRunning) - { - OnFinish(bSuccess); - MarkAsGarbage(); - GetManager()->FinishTask(this); - bFinished = true; - bSucceeded = bSuccess; - } -} - -bool USaveSlotDataTask::IsScheduled() const -{ - return GetManager()->Tasks.Contains(this); -} - -USaveManager* USaveSlotDataTask::GetManager() const -{ - return Cast(GetOuter()); -} - -void USaveSlotDataTask::BakeAllFilters() -{ - TRACE_CPUPROFILER_EVENT_SCOPE(USaveSlotDataTask::BakeAllFilters); - SlotData->GlobalLevelFilter.BakeAllowedClasses(); - - if (SlotData->MainLevel.bOverrideGlobalFilter) - { - SlotData->MainLevel.Filter.BakeAllowedClasses(); - } - - for (const auto& Level : SlotData->SubLevels) - { - if (Level.bOverrideGlobalFilter) - { - Level.Filter.BakeAllowedClasses(); - } - } -} - -const FSELevelFilter& USaveSlotDataTask::GetGlobalFilter() const -{ - check(SlotData); - return SlotData->GlobalLevelFilter; -} - -const FSELevelFilter& USaveSlotDataTask::GetLevelFilter(const FLevelRecord& Level) const -{ - if (Level.bOverrideGlobalFilter) - { - return Level.Filter; - } - return GetGlobalFilter(); -} - -FLevelRecord* USaveSlotDataTask::FindLevelRecord(const ULevelStreaming* Level) const -{ - if (!Level) - return &SlotData->MainLevel; - else // Find the Sub-Level - return SlotData->SubLevels.FindByKey(Level); -} - -UWorld* USaveSlotDataTask::GetWorld() const -{ - return GetOuter()->GetWorld(); -} diff --git a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp b/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp deleted file mode 100644 index 9e460ab..0000000 --- a/Source/SaveExtension/Private/Serialization/SlotDataTask_LevelLoader.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Serialization/SlotDataTask_LevelLoader.h" - - -///////////////////////////////////////////////////// -// USaveDataTask_LevelLoader - -void USaveSlotDataTask_LevelLoader::OnStart() -{ - if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) - { - FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); - if (!LevelRecord) - { - Finish(false); - return; - } - - GetLevelFilter(*LevelRecord).BakeAllowedClasses(); - - if (Slot->IsFrameSplitLoad()) - { - DeserializeLevelASync(StreamingLevel->GetLoadedLevel(), StreamingLevel); - } - else - { - DeserializeLevelSync(StreamingLevel->GetLoadedLevel(), StreamingLevel); - FinishedDeserializing(); - } - return; - } - Finish(false); -} - -void USaveSlotDataTask_LevelLoader::DeserializeASyncLoop(float StartMS /*= 0.0f*/) -{ - FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); - - if (StartMS <= 0) - { - StartMS = GetTimeMilliseconds(); - } - - const auto& Filter = GetLevelFilter(LevelRecord); - - // Continue Iterating actors every tick - for (; CurrentActorIndex < CurrentLevelActors.Num(); ++CurrentActorIndex) - { - AActor* Actor{CurrentLevelActors[CurrentActorIndex].Get()}; - if (Actor) - { - DeserializeLevel_Actor(Actor, LevelRecord, Filter); - - const float CurrentMS = GetTimeMilliseconds(); - // If x milliseconds passed, continue on next frame - if (CurrentMS - StartMS >= MaxFrameMs) - return; - } - } - - // All levels deserialized - FinishedDeserializing(); -} diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 5a3641f..8748961 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -29,29 +29,17 @@ struct FSELevelFilter UPROPERTY(SaveGame) FSEActorClassFilter ActorFilter; - UPROPERTY(SaveGame) - FSEActorClassFilter LoadActorFilter; - - UPROPERTY(SaveGame) - bool bStoreComponents = false; - UPROPERTY(SaveGame) FSEComponentClassFilter ComponentFilter; - UPROPERTY(SaveGame) - FSEComponentClassFilter LoadComponentFilter; - FSELevelFilter() = default; - void FromSlot(const USaveSlot& Slot); - void BakeAllowedClasses() const; - bool ShouldSave(const AActor* Actor) const; - bool ShouldSave(const UActorComponent* Component) const; - bool ShouldLoad(const AActor* Actor) const; - bool ShouldLoad(const UActorComponent* Component) const; + bool Stores(const AActor* Actor) const; + bool StoresAnyComponents() const; + bool Stores(const UActorComponent* Component) const; static bool StoresTransform(const UActorComponent* Component); static bool StoresTags(const UActorComponent* Component); diff --git a/Source/SaveExtension/Public/Misc/ClassFilter.h b/Source/SaveExtension/Public/Misc/ClassFilter.h index 06e1b16..7db8940 100644 --- a/Source/SaveExtension/Public/Misc/ClassFilter.h +++ b/Source/SaveExtension/Public/Misc/ClassFilter.h @@ -45,13 +45,18 @@ struct SAVEEXTENSION_API FSEClassFilter /** Bakes a set of allowed classes based on the current settings */ void BakeAllowedClasses() const; - FORCEINLINE bool IsClassAllowed(UClass* const Class) const + bool IsAllowed(UClass* Class) const { // Check is a single O(1) pointer hash comparison return BakedAllowedClasses.Contains(Class); } - FORCEINLINE UClass* GetBaseClass() const + bool IsAnyAllowed() const + { + return BakedAllowedClasses.Num() > 0; + } + + UClass* GetBaseClass() const { return BaseClass; } @@ -64,50 +69,20 @@ struct SAVEEXTENSION_API FSEClassFilter USTRUCT(BlueprintType) -struct FSEActorClassFilter +struct FSEActorClassFilter : public FSEClassFilter { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Filter) - FSEClassFilter ClassFilter; - - - FSEActorClassFilter() : ClassFilter(AActor::StaticClass()) {} - FSEActorClassFilter(TSubclassOf actorClass) : ClassFilter(actorClass) {} - - /** Bakes a set of allowed classes based on the current settings */ - void BakeAllowedClasses() const - { - ClassFilter.BakeAllowedClasses(); - } - - FORCEINLINE bool IsClassAllowed(UClass* const Class) const - { - return ClassFilter.IsClassAllowed(Class); - } + FSEActorClassFilter() : Super(AActor::StaticClass()) {} + FSEActorClassFilter(TSubclassOf ActorClass) : Super(ActorClass) {} }; USTRUCT(BlueprintType) -struct FSEComponentClassFilter +struct FSEComponentClassFilter : public FSEClassFilter { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Filter) - FSEClassFilter ClassFilter; - - - FSEComponentClassFilter() : ClassFilter(UActorComponent::StaticClass()) {} - FSEComponentClassFilter(TSubclassOf compClass) : ClassFilter(compClass) {} - - /** Bakes a set of allowed classes based on the current settings */ - void BakeAllowedClasses() const - { - ClassFilter.BakeAllowedClasses(); - } - - FORCEINLINE bool IsClassAllowed(UClass* const Class) const - { - return ClassFilter.IsClassAllowed(Class); - } + FSEComponentClassFilter() : Super(UActorComponent::StaticClass()) {} + FSEComponentClassFilter(TSubclassOf CompClass) : Super(CompClass) {} }; diff --git a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h index 5d0d6e7..24c928a 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h @@ -3,6 +3,7 @@ #pragma once #include "SaveFileHelpers.h" +#include "SaveManager.h" #include @@ -16,47 +17,27 @@ class FLoadFileTask : public FNonAbandonableTask TWeakObjectPtr Manager; const FString SlotName; + TWeakObjectPtr LastSlot; + TWeakObjectPtr LastSlotData; + TWeakObjectPtr Slot; - TWeakObjectPtr SlotData; public: - explicit FLoadFileTask(USaveManager* Manager, FStringView SlotName) : Manager(Manager), SlotName(SlotName) - {} - ~FLoadFileTask() - { - if (Slot.IsValid()) - { - Slot->ClearInternalFlags(EInternalObjectFlags::Async); - } - if (SlotData.IsValid()) - { - SlotData->ClearInternalFlags(EInternalObjectFlags::Async); - } - } + explicit FLoadFileTask(USaveManager* Manager, USaveSlot* LastSlot, FStringView SlotName); + ~FLoadFileTask(); - void DoWork() - { - FScopedFileReader FileReader(FSaveFileHelpers::GetSlotPath(SlotName)); - if (FileReader.IsValid()) - { - FSaveFile File; - File.Read(FileReader, false); - USaveSlot* NewSlot = Slot.Get(); - File.CreateAndDeserializeSlot(NewSlot, Manager.Get()); - File.CreateAndDeserializeData(NewSlot); - Slot = NewSlot; - } - } + void DoWork(); - USaveSlot* GetInfo() + /** Game thread */ + USaveSlot* GetInfo() const { return Slot.Get(); } - USaveSlotData* GetData() + USaveSlotData* GetData() const { - return SlotData.Get(); + return Slot.IsValid()? Slot->GetData() : nullptr; } TStatId GetStatId() const diff --git a/Source/SaveExtension/Public/SaveFileHelpers.h b/Source/SaveExtension/Public/SaveFileHelpers.h index 7daa676..53fff6d 100644 --- a/Source/SaveExtension/Public/SaveFileHelpers.h +++ b/Source/SaveExtension/Public/SaveFileHelpers.h @@ -99,8 +99,6 @@ struct FSaveFile void SerializeInfo(USaveSlot* Slot); void SerializeData(USaveSlotData* SlotData); - void CreateAndDeserializeSlot(USaveSlot*& Slot, const UObject* Outer) const; - void CreateAndDeserializeData(USaveSlot* Slot) const; }; @@ -120,6 +118,6 @@ class SAVEEXTENSION_API FSaveFileHelpers static FString GetSlotPath(FStringView SlotName); static FString GetThumbnailPath(FStringView SlotName); - static void DeserializeObject( - UObject*& Object, FStringView ClassName, const UObject* Outer, const TArray& Bytes); + static UObject* DeserializeObject( + UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes); }; diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index ff663b6..eb6392e 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -9,7 +9,7 @@ #include "SaveExtensionInterface.h" #include "SaveSlot.h" #include "SaveSlotData.h" -#include "Serialization/SlotDataTask.h" +#include "Serialization/SEDataTask.h" #include #include @@ -69,7 +69,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi { GENERATED_BODY() - friend USaveSlotDataTask; + friend FSEDataTask; /************************************************************************/ @@ -96,8 +96,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UPROPERTY(Transient) TArray> SubscribedInterfaces; - UPROPERTY(Transient) - TArray Tasks; + TArray> Tasks; /************************************************************************/ @@ -360,15 +359,15 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi void OnLevelLoaded(ULevelStreaming* StreamingLevel) {} - USaveSlotDataTask* CreateTask(TSubclassOf TaskType); - - template - TaskType* CreateTask() + template + TaskType& CreateTask() { - return Cast(CreateTask(TaskType::StaticClass())); + return static_cast( + *Tasks.Add_GetRef(MakeUnique(this, ActiveSlot)) + ); } - void FinishTask(USaveSlotDataTask* Task); + void FinishTask(FSEDataTask* Task); public: bool HasTasks() const @@ -417,10 +416,10 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(Category = SaveExtension, BlueprintCallable) void UnsubscribeFromEvents(const TScriptInterface& Interface); - void OnSaveBegan(const FSELevelFilter& Filter); - void OnSaveFinished(const FSELevelFilter& Filter, const bool bError); - void OnLoadBegan(const FSELevelFilter& Filter); - void OnLoadFinished(const FSELevelFilter& Filter, const bool bError); + void OnSaveBegan(); + void OnSaveFinished(const bool bError); + void OnLoadBegan(); + void OnLoadFinished(const bool bError); private: void OnMapLoadStarted(const FString& MapName); diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index af2a644..1ec8d33 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -17,9 +17,9 @@ struct FSELevelFilter; * Specifies the behavior while saving or loading */ UENUM() -enum class ESaveASyncMode : uint8 +enum class ESEAsyncMode : uint8 { - OnlySync, + SaveAndLoadSync, LoadAsync, SaveAsync, SaveAndLoadAsync @@ -101,31 +101,9 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") FSEActorClassFilter ActorFilter; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (PinHiddenByDefault, InlineEditConditionToggle)) - bool bUseLoadActorFilter = false; - - /** If enabled, this filter will be used while loading instead of "ActorFilter" */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (EditCondition = "bUseLoadActorFilter")) - FSEActorClassFilter LoadActorFilter; - - /** If true will store ActorComponents depending on the filters */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") - bool bStoreComponents = true; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") FSEComponentClassFilter ComponentFilter; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (PinHiddenByDefault, InlineEditConditionToggle)) - bool bUseLoadComponentFilter = false; - - /** If enabled, this filter will be used while loading instead of "ComponentFilter" */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization", - meta = (EditCondition = "bUseLoadComponentFilter")) - FSEComponentClassFilter LoadComponentFilter; - /** If true, will Save and Load levels when they are shown or hidden. * This includes level streaming and world composition. */ @@ -134,13 +112,13 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame /** Serialization will be multi-threaded between all available cores. */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") - ESaveASyncMode MultithreadedSerialization = ESaveASyncMode::SaveAsync; + ESEAsyncMode MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; /** Split serialization between multiple frames. Ignored if MultithreadedSerialization is used * Currently only implemented on Loading */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") - ESaveASyncMode FrameSplittedSerialization = ESaveASyncMode::OnlySync; + ESEAsyncMode FrameSplittedSerialization = ESEAsyncMode::SaveAndLoadSync; /** Max milliseconds to use every frame in an asynchronous operation. * If running at 60Fps (16.6ms), loading or saving asynchronously will add MaxFrameMS: @@ -148,13 +126,12 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame * This means gameplay will not be stopped nor have frame drops while saving or loading. Works best for * non multi-threaded platforms */ - UPROPERTY( - EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async", meta = (UIMin = "3", UIMax = "10")) + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async", meta = (UIMin = "3", UIMax = "10")) float MaxFrameMs = 5.f; /** Files will be loaded or saved on a secondary thread while game continues */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") - ESaveASyncMode MultithreadedFiles = ESaveASyncMode::SaveAndLoadAsync; + ESEAsyncMode MultithreadedFiles = ESEAsyncMode::SaveAndLoadAsync; /** * If checked, will print messages to Log, and Viewport if DebugInScreen is enabled. @@ -176,34 +153,33 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame public: /** Slot where this SaveInfo and its saveData are saved */ - UPROPERTY(BlueprintReadWrite, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = Slot) FName FileName = TEXT("Default"); - UPROPERTY(BlueprintReadWrite, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = Slot) FText DisplayName; /** Root Level where this Slot was saved */ - UPROPERTY(BlueprintReadOnly, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadOnly, Category = Slot) FName Map; - UPROPERTY(BlueprintReadWrite, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = Slot) FSaveSlotStats Stats; protected: - UPROPERTY() + UPROPERTY(SaveGame) FString ThumbnailPath; /** Thumbnail gets cached here the first time it is requested */ UPROPERTY(Transient) TObjectPtr CachedThumbnail; - UPROPERTY(BlueprintReadOnly, Transient, Category = Slot) + UPROPERTY(Transient, BlueprintReadOnly, Category = Slot) TObjectPtr Data; public: - USaveSlot(); - + void PostInitProperties() override; /** Returns this slot's thumbnail if any */ UFUNCTION(BlueprintCallable, Category = Slot) @@ -251,16 +227,10 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame virtual int32 OnGetIndex() const; public: - UFUNCTION(BlueprintPure, Category = SaveSlot) - const FSEActorClassFilter& GetActorFilter(bool bIsLoading) const; - - UFUNCTION(BlueprintPure, Category = SaveSlot) - const FSEComponentClassFilter& GetComponentFilter(bool bIsLoading) const; - bool IsMTSerializationLoad() const; bool IsMTSerializationSave() const; - ESaveASyncMode GetFrameSplitSerialization() const; + ESEAsyncMode GetFrameSplitSerialization() const; float GetMaxFrameMs() const; bool IsFrameSplitLoad() const; @@ -269,5 +239,14 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame bool IsMTFilesLoad() const; bool IsMTFilesSave() const; - FSELevelFilter ToFilter() const; + UFUNCTION(BlueprintPure, Category = SaveSlot) + bool IsLoadingOrSaving() const; + + // Called for every level before being saved or loaded + UFUNCTION(BlueprintNativeEvent, Category = Slot) + void GetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) const; + +private: + // Called for every level before being saved or loaded + virtual void OnGetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) const; }; diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index bd60669..e53e9b6 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -27,26 +27,16 @@ class SAVEEXTENSION_API USaveSlotData : public USaveGame GENERATED_BODY() public: - USaveSlotData() : Super() {} - - - /** Full Name of the Map where this SlotData was saved */ - UPROPERTY(Category = SaveData, BlueprintReadOnly, meta = (AllowPrivateAccess = "true")) - FName Map; - /** Game world time since game started in seconds */ - UPROPERTY(Category = SaveData, BlueprintReadOnly) + UPROPERTY(SaveGame, Category = SaveData, BlueprintReadOnly) float TimeSeconds; /** Records * All serialized information to be saved or loaded * Serialized manually for performance */ - bool bStoreGameInstance = false; FObjectRecord GameInstance; - - FSELevelFilter GlobalLevelFilter; - FPersistentLevelRecord MainLevel; + FPersistentLevelRecord RootLevel; TArray SubLevels; diff --git a/Source/SaveExtension/Public/Serialization/LevelRecords.h b/Source/SaveExtension/Public/Serialization/LevelRecords.h index 9be4385..232adae 100644 --- a/Source/SaveExtension/Public/Serialization/LevelRecords.h +++ b/Source/SaveExtension/Public/Serialization/LevelRecords.h @@ -19,16 +19,17 @@ struct FLevelRecord : public FBaseRecord { GENERATED_BODY() - bool bOverrideGlobalFilter = false; - // Filter is used if bOverrideGlobalFilter is true - FSELevelFilter Filter; - /** Record of the Level Script Actor */ FActorRecord LevelScript; /** Records of the World Actors */ TArray Actors; + /** Not-serialized. Assigned before loading and saving by the SaveSlot */ + FSELevelFilter Filter; + + /** Not-serialized. During saving or loading points to the live actor */ + TArray>> RecordsToActors; FLevelRecord() : Super() {} diff --git a/Source/SaveExtension/Public/Serialization/MTTask.h b/Source/SaveExtension/Public/Serialization/MTTask.h index 2f19814..f480bfa 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask.h +++ b/Source/SaveExtension/Public/Serialization/MTTask.h @@ -16,19 +16,14 @@ // Async task to serialize actors from a level. class FMTTask : public FNonAbandonableTask { -protected: +public: /** Used only if Sync */ - const UWorld* const World; - USaveSlotData* SlotData; + UWorld* const World = nullptr; + USaveSlotData* SlotData = nullptr; - // Locally cached settings - const FSELevelFilter& Filter; - - FMTTask( - const bool bIsloading, const UWorld* InWorld, USaveSlotData* InSlotData, const FSELevelFilter& Filter) - : World(InWorld) - , SlotData(InSlotData) - , Filter(Filter) + FMTTask(const bool bIsloading, UWorld* World, USaveSlotData* SlotData) + : World(World) + , SlotData(SlotData) {} }; diff --git a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h index 03de370..638ae1d 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h +++ b/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h @@ -27,30 +27,30 @@ DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); // Async task to serialize actors from a level. class FMTTask_SerializeActors : public FMTTask { - const TArray* const LevelActors; - const int32 StartIndex; - const int32 Num; + const TArray* LevelActors; + const int32 StartIndex = 0; + const int32 Num = 0; const bool bStoreGameInstance = false; - /** USE ONLY FOR DUMPING DATA */ FLevelRecord* LevelRecord = nullptr; + const FSELevelFilter* Filter = nullptr; + FActorRecord LevelScriptRecord; TArray ActorRecords; public: - FMTTask_SerializeActors(const UWorld* World, USaveSlotData* SlotData, - const TArray* const InLevelActors, const int32 InStartIndex, const int32 InNum, - bool bStoreGameInstance, FLevelRecord* InLevelRecord, const FSELevelFilter& Filter) - : FMTTask(false, World, SlotData, Filter) - , LevelActors(InLevelActors) - , StartIndex(InStartIndex) - , Num(InNum) + FMTTask_SerializeActors(UWorld* World, USaveSlotData* SlotData, + const TArray* LevelActors, const int32 StartIndex, const int32 Num, + bool bStoreGameInstance, FLevelRecord* LevelRecord, const FSELevelFilter* Filter) + : FMTTask(false, World, SlotData) + , LevelActors(LevelActors) + , StartIndex(StartIndex) + , Num(Num) , bStoreGameInstance(bStoreGameInstance) - , LevelRecord(InLevelRecord) - , LevelScriptRecord{} - , ActorRecords{} + , LevelRecord(LevelRecord) + , Filter(Filter) { // No apparent performance benefit // ActorRecords.Reserve(Num); diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index a0fdfdd..8c3704f 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -41,7 +41,7 @@ struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 0; + return !Name.IsNone() && Class; } - FORCEINLINE bool operator==(const UObject* Other) const + bool operator==(const UObject* Other) const { return Other && Name == Other->GetFName() && Class == Other->GetClass(); } diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask.h b/Source/SaveExtension/Public/Serialization/SEDataTask.h similarity index 66% rename from Source/SaveExtension/Public/Serialization/SlotDataTask.h rename to Source/SaveExtension/Public/Serialization/SEDataTask.h index 8e8172f..23d70ee 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask.h @@ -10,42 +10,44 @@ #include #include -#include "SlotDataTask.generated.h" - class USaveManager; +enum class ESETaskType : uint8 +{ + None, + Load, + Save +}; /** - * Base class for managing a SaveData file + * Base class for managing the data of SaveSlot file */ -UCLASS() -class USaveSlotDataTask : public UObject +struct FSEDataTask { - GENERATED_BODY() - + ESETaskType Type = ESETaskType::None; private: - uint8 bRunning : 1; - uint8 bFinished : 1; - uint8 bSucceeded : 1; + bool bRunning = false; + bool bFinished = false; + bool bSucceeded = false; protected: - UPROPERTY() - USaveSlotData* SlotData; - UPROPERTY() + TObjectPtr Manager; + TObjectPtr SlotData; float MaxFrameMs = 0.f; -public: - USaveSlotDataTask() : Super(), bRunning(false), bFinished(false) {} - void Prepare(USaveSlot* Slot) - { - SlotData = Slot->GetData(); - MaxFrameMs = Slot->GetMaxFrameMs(); - } +public: + FSEDataTask(USaveManager* Manager, USaveSlot* Slot, ESETaskType Type) + : Type(Type) + , Manager(Manager) + , SlotData(Slot->GetData()) + , MaxFrameMs(Slot->GetMaxFrameMs()) + {} + virtual ~FSEDataTask() = default; - USaveSlotDataTask* Start(); + FSEDataTask& Start(); virtual void Tick(float DeltaTime) {} @@ -76,23 +78,16 @@ class USaveSlotDataTask : public UObject virtual void OnFinish(bool bSuccess) {} - USaveManager* GetManager() const; - void BakeAllFilters(); - const FSELevelFilter& GetGlobalFilter() const; - const FSELevelFilter& GetLevelFilter(const FLevelRecord& Level) const; - FLevelRecord* FindLevelRecord(const ULevelStreaming* Level) const; - //~ Begin UObject Interface - virtual UWorld* GetWorld() const override; - //~ End UObject Interface - - FORCEINLINE float GetTimeMilliseconds() const + float GetTimeMilliseconds() const { return FPlatformTime::ToMilliseconds(FPlatformTime::Cycles()); } + + UWorld* GetWorld() const; }; diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h similarity index 73% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index 39c27ce..d7c7c6f 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Loader.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -7,7 +7,7 @@ #include "Multithreading/LoadFileTask.h" #include "SaveSlot.h" #include "SaveSlotData.h" -#include "SlotDataTask.h" +#include "SEDataTask.h" #include #include @@ -15,8 +15,6 @@ #include #include -#include "SlotDataTask_Loader.generated.h" - enum class ELoadDataTaskState : uint8 { @@ -33,16 +31,12 @@ enum class ELoadDataTaskState : uint8 /** * Manages the loading process of a SaveData file */ -UCLASS() -class USaveSlotDataTask_Loader : public USaveSlotDataTask +struct FSEDataTask_Load : public FSEDataTask { - GENERATED_BODY() - protected: FName SlotName; - UPROPERTY() - USaveSlot* Slot; + TObjectPtr Slot; FOnGameLoaded Delegate; @@ -62,18 +56,21 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask public: - USaveSlotDataTask_Loader() : Super() {} + FSEDataTask_Load(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask(Manager, Slot, ESETaskType::Load) + {} + ~FSEDataTask_Load(); - auto Setup(FName InSlotName) + auto& Setup(FName InSlotName) { SlotName = InSlotName; - return this; + return *this; } - auto Bind(const FOnGameLoaded& OnLoaded) + auto& Bind(const FOnGameLoaded& OnLoaded) { Delegate = OnLoaded; - return this; + return *this; } void OnMapLoaded(); @@ -83,19 +80,18 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask virtual void Tick(float DeltaTime) override; virtual void OnFinish(bool bSuccess) override; - virtual void BeginDestroy() override; void StartDeserialization(); /** Spawns Actors hat were saved but which actors are not in the world. */ - void RespawnActors(const TArray& Records, const ULevel* Level); + void RespawnActors(const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord); protected: //~ Begin Files void StartLoadingData(); USaveSlotData* GetLoadedData() const; - FORCEINLINE const bool IsDataLoaded() const + const bool IsDataLoaded() const { return LoadDataTask && LoadDataTask->IsDone(); }; @@ -117,22 +113,17 @@ class USaveSlotDataTask_Loader : public USaveSlotDataTask void PrepareAllLevels(); void PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord); - /** Deserializes all Level actors. */ - inline void DeserializeLevel_Actor( - AActor* const Actor, const FLevelRecord& LevelRecord, const FSELevelFilter& Filter); - void FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const; -private: /** Deserializes Game Instance Object and its Properties. - Requires 'SaveGameMode' flag to be used. */ + * Requires 'SaveGameInstance' flag to be used. + */ void DeserializeGameInstance(); /** Serializes an actor into this Actor Record */ - bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter); + bool DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord); /** Deserializes the components of an actor from a provided Record */ - void DeserializeActorComponents( - AActor* Actor, const FActorRecord& ActorRecord, const FSELevelFilter& Filter, int8 indent = 0); + void DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 indent = 0); /** END Deserialization */ }; \ No newline at end of file diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h b/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h similarity index 52% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h index dbb7a13..f2a5665 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelLoader.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h @@ -3,28 +3,26 @@ #pragma once #include "ISaveExtension.h" -#include "SlotDataTask_Loader.h" - -#include "SlotDataTask_LevelLoader.generated.h" +#include "SEDataTask_Load.h" /** * Manages the serializing process of a single level */ -UCLASS() -class USaveSlotDataTask_LevelLoader : public USaveSlotDataTask_Loader +struct FSEDataTask_LoadLevel : public FSEDataTask_Load { - GENERATED_BODY() - + TObjectPtr StreamingLevel; - UPROPERTY() - ULevelStreaming* StreamingLevel; public: - auto Setup(ULevelStreaming* InStreamingLevel) + FSEDataTask_LoadLevel(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask_Load(Manager, Slot) + {} + + auto& Setup(ULevelStreaming* InStreamingLevel) { StreamingLevel = InStreamingLevel; - return this; + return *this; } private: diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h similarity index 76% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 529ae76..5dd1561 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_Saver.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -7,7 +7,7 @@ #include "MTTask_SerializeActors.h" #include "Multithreading/SaveFileTask.h" #include "SaveSlotData.h" -#include "SlotDataTask.h" +#include "SEDataTask.h" #include #include @@ -17,46 +17,42 @@ #include #include -#include "SlotDataTask_Saver.generated.h" - /** * Manages the saving process of a SaveData file */ -UCLASS() -class USaveSlotDataTask_Saver : public USaveSlotDataTask +struct FSEDataTask_Save : public FSEDataTask { - GENERATED_BODY() - - bool bOverride; - bool bSaveThumbnail; + bool bOverride = false; + bool bSaveThumbnail = false; FName SlotName; - int32 Width; - int32 Height; + int32 Width = 0; + int32 Height = 0; FOnGameSaved Delegate; protected: - UPROPERTY() - USaveSlot* Slot; + TObjectPtr Slot; /** Start Async variables */ TWeakObjectPtr CurrentLevel; TWeakObjectPtr CurrentSLevel; - int32 CurrentActorIndex; TArray> CurrentLevelActors; /** End Async variables */ /** Begin AsyncTasks */ TArray> Tasks; - FAsyncTask* SaveTask; + FAsyncTask* SaveTask = nullptr; /** End AsyncTasks */ public: - USaveSlotDataTask_Saver() : USaveSlotDataTask(), SaveTask(nullptr) {} + FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask(Manager, Slot, ESETaskType::Save) + {} + ~FSEDataTask_Save(); - auto* Setup( + auto& Setup( FName InSlotName, bool bInOverride, bool bInSaveThumbnail, const int32 InWidth, const int32 InHeight) { SlotName = InSlotName; @@ -65,20 +61,19 @@ class USaveSlotDataTask_Saver : public USaveSlotDataTask Width = InWidth; Height = InHeight; - return this; + return *this; } - auto* Bind(const FOnGameSaved& OnSaved) + auto& Bind(const FOnGameSaved& OnSaved) { Delegate = OnSaved; - return this; + return *this; } // Where all magic happens virtual void OnStart() override; virtual void Tick(float DeltaTime) override; virtual void OnFinish(bool bSuccess) override; - virtual void BeginDestroy() override; protected: /** BEGIN Serialization */ @@ -86,6 +81,7 @@ class USaveSlotDataTask_Saver : public USaveSlotDataTask void SerializeWorld(); void PrepareAllLevels(const TArray& Levels); + void PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord); void SerializeLevelSync( const ULevel* Level, int32 AssignedThreads, const ULevelStreaming* StreamingLevel = nullptr); diff --git a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h b/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h similarity index 55% rename from Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h rename to Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h index 8477509..ede2300 100644 --- a/Source/SaveExtension/Public/Serialization/SlotDataTask_LevelSaver.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h @@ -3,28 +3,26 @@ #pragma once #include "ISaveExtension.h" -#include "SlotDataTask_Saver.h" - -#include "SlotDataTask_LevelSaver.generated.h" +#include "SEDataTask_Save.h" /** * Manages the serializing process of a single level */ -UCLASS() -class USaveSlotDataTask_LevelSaver : public USaveSlotDataTask_Saver +struct FSEDataTask_SaveLevel : public FSEDataTask_Save { - GENERATED_BODY() - + TObjectPtr StreamingLevel; - UPROPERTY() - ULevelStreaming* StreamingLevel; public: - auto Setup(ULevelStreaming* InStreamingLevel) + FSEDataTask_SaveLevel(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask_Save(Manager, Slot) + {} + + auto& Setup(ULevelStreaming* InStreamingLevel) { StreamingLevel = InStreamingLevel; - return this; + return *this; } private: diff --git a/Source/Test/Private/Files.spec.cpp b/Source/Test/Private/Files.spec.cpp index 37f281f..97da352 100644 --- a/Source/Test/Private/Files.spec.cpp +++ b/Source/Test/Private/Files.spec.cpp @@ -34,11 +34,11 @@ void FSaveSpec_Files::Define() SaveManager->bTickWithGameWorld = true; - SaveManager->GetActiveSlot()->MultithreadedSerialization = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; }); It("Can save files synchronously", [this]() { - SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; TestTrue("Saved", SaveManager->SaveSlot(0)); @@ -46,7 +46,7 @@ void FSaveSpec_Files::Define() }); It("Can save files asynchronously", [this]() { - SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::SaveAsync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESEAsyncMode::SaveAsync; bFinishTick = false; bool bSaving = @@ -66,7 +66,7 @@ void FSaveSpec_Files::Define() }); It("Can load files synchronously", [this]() { - SaveManager->GetActiveSlot()->MultithreadedFiles = ESaveASyncMode::OnlySync; + SaveManager->GetActiveSlot()->MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; TestTrue("Saved", SaveManager->SaveSlot(0)); diff --git a/Source/Test/Private/GameInstanceSpec.h b/Source/Test/Private/GameInstanceSpec.h index 92e7fca..afe795c 100644 --- a/Source/Test/Private/GameInstanceSpec.h +++ b/Source/Test/Private/GameInstanceSpec.h @@ -17,7 +17,7 @@ class UTestSaveSlot : public USaveSlot { bStoreGameInstance = true; - MultithreadedFiles = ESaveASyncMode::OnlySync; - MultithreadedSerialization = ESaveASyncMode::OnlySync; + MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; + MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; } }; diff --git a/Source/Test/Private/SavingSpec.h b/Source/Test/Private/SavingSpec.h index f16b5e0..92b07b7 100644 --- a/Source/Test/Private/SavingSpec.h +++ b/Source/Test/Private/SavingSpec.h @@ -17,8 +17,8 @@ class UTestSaveSlot_SyncSaving : public USaveSlot { bStoreGameInstance = true; - MultithreadedFiles = ESaveASyncMode::OnlySync; - MultithreadedSerialization = ESaveASyncMode::OnlySync; - ActorFilter.ClassFilter.AllowedClasses.Add(ATestActor::StaticClass()); + MultithreadedFiles = ESEAsyncMode::SaveAndLoadSync; + MultithreadedSerialization = ESEAsyncMode::SaveAndLoadSync; + ActorFilter.AllowedClasses.Add(ATestActor::StaticClass()); } }; From 826b72a773c76c9793dfea53bce235ff4dee1330 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 3 Oct 2023 12:28:05 +0200 Subject: [PATCH 7/7] Moved tasks to correct folder, removed SlotIds --- .../Private/LifetimeComponent.cpp | 2 +- .../MTTask_SerializeActors.cpp | 2 +- .../Multithreading/ScopedTaskManager.cpp | 3 - Source/SaveExtension/Private/SaveManager.cpp | 16 +- .../Private/Serialization/MTTask.cpp | 3 - .../MTTask.h | 0 .../MTTask_SerializeActors.h | 4 +- Source/SaveExtension/Public/SaveManager.h | 153 ++---------------- .../Public/Serialization/SEDataTask_Save.h | 2 +- 9 files changed, 21 insertions(+), 164 deletions(-) rename Source/SaveExtension/Private/{Serialization => Multithreading}/MTTask_SerializeActors.cpp (98%) delete mode 100644 Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp delete mode 100644 Source/SaveExtension/Private/Serialization/MTTask.cpp rename Source/SaveExtension/Public/{Serialization => Multithreading}/MTTask.h (100%) rename Source/SaveExtension/Public/{Serialization => Multithreading}/MTTask_SerializeActors.h (96%) diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index a710974..d2e235a 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -2,7 +2,7 @@ #include "LifetimeComponent.h" -#include "Serialization/MTTask.h" +#include "Multithreading/MTTask.h" diff --git a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp b/Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp similarity index 98% rename from Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp rename to Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp index f8a91f9..8fc945a 100644 --- a/Source/SaveExtension/Private/Serialization/MTTask_SerializeActors.cpp +++ b/Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Serialization/MTTask_SerializeActors.h" +#include "Multithreading/MTTask_SerializeActors.h" #include "SaveManager.h" #include "SaveSlot.h" diff --git a/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp b/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp deleted file mode 100644 index e29b16f..0000000 --- a/Source/SaveExtension/Private/Multithreading/ScopedTaskManager.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Multithreading/ScopedTaskManager.h" diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 62bb91e..e5160e0 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -298,7 +298,7 @@ void USaveManager::DeleteAllSlots(FOnSlotsDeleted Delegate) .StartBackgroundTask(); } -void USaveManager::BPSaveSlot(FName SlotName, bool bScreenshot, const FScreenshotSize Size, +void USaveManager::BPSaveSlotByName(FName SlotName, bool bScreenshot, const FScreenshotSize Size, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded /*= true*/) { if (UWorld* World = GetWorld()) @@ -316,7 +316,7 @@ void USaveManager::BPSaveSlot(FName SlotName, bool bScreenshot, const FScreensho Result = ESEContinueOrFail::Failed; } -void USaveManager::BPLoadSlot(FName SlotName, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) +void USaveManager::BPLoadSlotByName(FName SlotName, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) { @@ -460,18 +460,6 @@ void USaveManager::FinishTask(FSEDataTask* Task) } } -FName USaveManager::GetFileNameFromId(const int32 SlotId) const -{ - // TODO: Expose custom names - // if (const auto* Preset = GetActivePreset()) - //{ - // FName Name; - // Preset->BPGetSlotNameFromId(SlotId, Name); - // return Name; - //} - return FName{FString::FromInt(SlotId)}; -} - bool USaveManager::IsLoading() const { return HasTasks() && Tasks[0]->Type == ESETaskType::Load; diff --git a/Source/SaveExtension/Private/Serialization/MTTask.cpp b/Source/SaveExtension/Private/Serialization/MTTask.cpp deleted file mode 100644 index 6cc36f9..0000000 --- a/Source/SaveExtension/Private/Serialization/MTTask.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Serialization/MTTask.h" diff --git a/Source/SaveExtension/Public/Serialization/MTTask.h b/Source/SaveExtension/Public/Multithreading/MTTask.h similarity index 100% rename from Source/SaveExtension/Public/Serialization/MTTask.h rename to Source/SaveExtension/Public/Multithreading/MTTask.h diff --git a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h b/Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h similarity index 96% rename from Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h rename to Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h index 638ae1d..14568df 100644 --- a/Source/SaveExtension/Public/Serialization/MTTask_SerializeActors.h +++ b/Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h @@ -2,8 +2,8 @@ #pragma once -#include "MTTask.h" -#include "MTTask_SerializeActors.h" +#include "Multithreading/MTTask.h" +#include "Multithreading/MTTask_SerializeActors.h" #include "Serialization/LevelRecords.h" #include "Serialization/Records.h" diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index eb6392e..672eaa9 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -127,10 +127,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi bool SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded = true, bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); - /** Save the Game into an specified slot id */ - bool SaveSlot(int32 SlotId, bool bOverrideIfNeeded = true, bool bScreenshot = false, - const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); - /** Save the currently loaded Slot */ bool SaveCurrentSlot( bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); @@ -139,9 +135,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Load game from a file name */ bool LoadSlot(FName SlotName, FOnGameLoaded OnLoaded = {}); - /** Load game from a slot Id */ - bool LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded = {}); - /** Load game from a Slot */ bool LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded = {}); @@ -175,25 +168,17 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Save the Game into an specified Slot */ UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, - meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot", Latent, - LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlot(FName SlotName, bool bScreenshot, - UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, - FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); - - /** Save the Game into an specified Slot */ - UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, - meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Id", Latent, + meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Name", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotById(int32 SlotId, bool bScreenshot, + void BPSaveSlotByName(FName SlotName, bool bScreenshot, UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); /** Save the Game to a Slot */ UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, - meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot by Info", Latent, + meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreenshot, + void BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true); @@ -205,26 +190,20 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { - BPSaveSlotByInfo(ActiveSlot, bScreenshot, Size, Result, MoveTemp(LatentInfo), true); + BPSaveSlot(ActiveSlot, bScreenshot, Size, Result, MoveTemp(LatentInfo), true); } /** Load game from a slot name */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Load Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", + meta = (DisplayName = "Load Slot by Name", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlot(FName SlotName, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); - - /** Load game from a slot Id */ - UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Load Slot by Id", Latent, LatentInfo = "LatentInfo", - ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotById(int32 SlotId, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); + void BPLoadSlotByName(FName SlotName, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Load game from a Slot */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Load Slot by Info", Latent, LatentInfo = "LatentInfo", + meta = (DisplayName = "Load Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotByInfo(const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); + void BPLoadSlot(const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Reload the currently loaded slot if any */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", @@ -232,7 +211,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPReloadCurrentSlot(ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { - BPLoadSlotByInfo(ActiveSlot, Result, MoveTemp(LatentInfo)); + BPLoadSlot(ActiveSlot, Result, MoveTemp(LatentInfo)); } /** @@ -246,15 +225,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi void BPFindAllSlots(const bool bSortByRecent, TArray& Slots, ESEContinue& Result, struct FLatentActionInfo LatentInfo); - /** Delete a saved game on an specified slot Id - * Performance: Interacts with disk, can be slow - */ - UFUNCTION(BlueprintCallable, Category = "SaveExtension") - FORCEINLINE bool DeleteSlotById(int32 SlotId) - { - return DeleteSlot(GetFileNameFromId(SlotId)); - } - /** Delete all saved slots from disk, loaded or not */ UFUNCTION(BlueprintCallable, Category = "SaveExtension", meta = (Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", @@ -281,18 +251,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi return ActiveSlot; } - /** - * Load and return an Slot by Id if it exists - * Performance: Interacts with disk, could be slow if called frequently - * @param SlotId Id of the Slot to be loaded - * @return the Slot associated with an Id - */ - UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") - FORCEINLINE USaveSlot* GetSlotById(int32 SlotId) - { - return LoadInfo(SlotId); - } - UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") FORCEINLINE USaveSlot* GetSlot(FName SlotName) { @@ -305,18 +263,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi UFUNCTION(BlueprintPure, Category = "SaveExtension|Slots") bool IsSlotSaved(FName SlotName) const; - /** Check if an slot exists on disk - * @return true if the slot exists - */ - UFUNCTION(BlueprintPure, Category = "SaveExtension|Slots") - bool IsSlotSavedById(int32 SlotId) const - { - if (ActiveSlot && ActiveSlot->IsValidIndex(SlotId)) - { - return IsSlotSaved(GetFileNameFromId(SlotId)); - } - return false; - } /** Check if currently playing in a saved slot * @return true if currently playing in a saved slot @@ -329,20 +275,12 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi void AssureActiveSlot(TSubclassOf ActiveSlotClass = {}, bool bForced = false); - UFUNCTION(BlueprintPure, Category = "SaveExtension") - FName GetFileNameFromId(const int32 SlotId) const; - - void AssignActiveSlot(USaveSlot* NewInfo) { ActiveSlot = NewInfo; } USaveSlot* LoadInfo(FName FileName); - USaveSlot* LoadInfo(uint32 SlotId) - { - return LoadInfo(GetFileNameFromId(SlotId)); - } protected: bool CanLoadOrSave(); @@ -434,44 +372,9 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi public: /** Get the global save manager */ static USaveManager* Get(const UObject* ContextObject); - - - /***********************************************************************/ - /* DEPRECATED */ - /***********************************************************************/ - - UFUNCTION(Category = "SaveExtension|Saving", BlueprintCallable, - meta = (DeprecatedFunction, DeprecationMessage = "Use 'Save Slot by Id' instead.", - AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Slot to Id", Latent, - LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveSlotToId(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, ESEContinueOrFail& Result, - FLatentActionInfo LatentInfo, bool bOverrideIfNeeded = true) - { - BPSaveSlotById(SlotId, bScreenshot, Size, Result, LatentInfo, bOverrideIfNeeded); - } - - UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DeprecatedFunction, DeprecationMessage = "Use 'Load Slot by Id' instead.", - DisplayName = "Load Slot from Id", Latent, LatentInfo = "LatentInfo", - ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPLoadSlotFromId(int32 SlotId, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) - { - BPLoadSlotById(SlotId, Result, LatentInfo); - } }; -inline bool USaveManager::SaveSlot( - int32 SlotId, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) -{ - if (!ActiveSlot->IsValidIndex(SlotId)) - { - UE_LOG(LogSaveExtension, Error, TEXT("Can't save to slot id under 0 or exceeding MaxSlots.")); - return false; - } - return SaveSlot(GetFileNameFromId(SlotId), bOverrideIfNeeded, bScreenshot, Size, OnSaved); -} - inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded, bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { @@ -482,19 +385,7 @@ inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded return SaveSlot(Slot->FileName, bOverrideIfNeeded, bScreenshot, Size, OnSaved); } -inline void USaveManager::BPSaveSlotById(int32 SlotId, bool bScreenshot, const FScreenshotSize Size, - ESEContinueOrFail& Result, FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) -{ - if (!ActiveSlot || !ActiveSlot->IsValidIndex(SlotId)) - { - UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Cant go under 0 or exceed MaxSlots.")); - Result = ESEContinueOrFail::Failed; - return; - } - BPSaveSlot(GetFileNameFromId(SlotId), bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); -} - -inline void USaveManager::BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreenshot, +inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, const FScreenshotSize Size, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { @@ -503,7 +394,7 @@ inline void USaveManager::BPSaveSlotByInfo(const USaveSlot* Slot, bool bScreensh Result = ESEContinueOrFail::Failed; return; } - BPSaveSlot(Slot->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); + BPSaveSlotByName(Slot->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } /** Save the currently loaded Slot */ @@ -512,16 +403,6 @@ inline bool USaveManager::SaveCurrentSlot(bool bScreenshot, const FScreenshotSiz return SaveSlot(ActiveSlot, true, bScreenshot, Size, OnSaved); } -inline bool USaveManager::LoadSlot(int32 SlotId, FOnGameLoaded OnLoaded) -{ - if (!ActiveSlot->IsValidIndex(SlotId)) - { - UE_LOG(LogSaveExtension, Error, TEXT("Invalid Slot. Can't go under 0 or exceed MaxSlots.")); - return false; - } - return LoadSlot(GetFileNameFromId(SlotId), OnLoaded); -} - inline bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded) { if (!Slot) @@ -531,13 +412,7 @@ inline bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded return LoadSlot(Slot->FileName, OnLoaded); } -inline void USaveManager::BPLoadSlotById( - int32 SlotId, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) -{ - BPLoadSlot(GetFileNameFromId(SlotId), Result, MoveTemp(LatentInfo)); -} - -inline void USaveManager::BPLoadSlotByInfo( +inline void USaveManager::BPLoadSlot( const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { if (!Slot) @@ -545,7 +420,7 @@ inline void USaveManager::BPLoadSlotByInfo( Result = ESEContinueOrFail::Failed; return; } - BPLoadSlot(Slot->FileName, Result, MoveTemp(LatentInfo)); + BPLoadSlotByName(Slot->FileName, Result, MoveTemp(LatentInfo)); } inline void USaveManager::IterateSubscribedInterfaces(TFunction&& Callback) diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 5dd1561..32939cd 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -4,7 +4,7 @@ #include "Delegates.h" #include "ISaveExtension.h" -#include "MTTask_SerializeActors.h" +#include "Multithreading/MTTask_SerializeActors.h" #include "Multithreading/SaveFileTask.h" #include "SaveSlotData.h" #include "SEDataTask.h"