From 227c9b1dfa22bb7b1a090bc89f1a5089bebb91b9 Mon Sep 17 00:00:00 2001 From: muit Date: Sat, 16 Sep 2023 11:42:29 +0200 Subject: [PATCH 01/39] 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 02/39] 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 03/39] 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 04/39] 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 05/39] 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 06/39] 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 7458d76bf3d08ffd38c63167db3c41184451c4f0 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 3 Oct 2023 12:17:31 +0200 Subject: [PATCH 07/39] Added subsystem records --- Source/SaveExtension/Public/SaveSlotData.h | 2 ++ Source/SaveExtension/Public/Serialization/Records.h | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index e53e9b6..404deec 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -39,6 +39,8 @@ class SAVEEXTENSION_API USaveSlotData : public USaveGame FPersistentLevelRecord RootLevel; TArray SubLevels; + TArray GameInstanceSubsystems; + TArray WorldSubsystems; void CleanRecords(bool bKeepSublevels); diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index 8c3704f..22ef378 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -110,3 +110,14 @@ struct FActorRecord : public FObjectRecord virtual bool Serialize(FArchive& Ar) override; }; + + +/** Represents a serialized Subsystem */ +USTRUCT() +struct FSubsystemRecord : public FObjectRecord +{ + GENERATED_BODY() + + FSubsystemRecord() : Super() {} + FSubsystemRecord(const USubsystem* Subsystem) : Super(Subsystem) {} +}; \ No newline at end of file From 826b72a773c76c9793dfea53bce235ff4dee1330 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 3 Oct 2023 12:28:05 +0200 Subject: [PATCH 08/39] 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" From 06b6076ce627fe82ebb3769334cf8c8cc4fcfb04 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 3 Oct 2023 19:47:32 +0200 Subject: [PATCH 09/39] Refactor async serialization --- SaveExtension.uplugin | 2 +- .../Private/LifetimeComponent.cpp | 1 - .../Multithreading/MTTask_SerializeActors.cpp | 158 -------------- Source/SaveExtension/Private/SaveSlot.cpp | 8 +- .../Private/Serialization/SEDataTask_Save.cpp | 201 +++++++++++------- .../Serialization/SEDataTask_SaveLevel.cpp | 5 +- .../Public/Multithreading/LoadFileTask.h | 1 - .../Public/Multithreading/MTTask.h | 29 --- .../Multithreading/MTTask_SerializeActors.h | 85 -------- Source/SaveExtension/Public/SaveSlot.h | 4 +- .../Public/Serialization/Records.h | 3 +- .../Public/Serialization/SEDataTask_Save.h | 15 +- 12 files changed, 141 insertions(+), 371 deletions(-) delete mode 100644 Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp delete mode 100644 Source/SaveExtension/Public/Multithreading/MTTask.h delete mode 100644 Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h diff --git a/SaveExtension.uplugin b/SaveExtension.uplugin index 3db46d6..4316657 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": "5.2", + "EngineVersion": "5.3", "EnabledByDefault": true, "CanContainContent": false, "IsBetaVersion": false, diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index d2e235a..0044606 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -2,7 +2,6 @@ #include "LifetimeComponent.h" -#include "Multithreading/MTTask.h" diff --git a/Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp b/Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp deleted file mode 100644 index 8fc945a..0000000 --- a/Source/SaveExtension/Private/Multithreading/MTTask_SerializeActors.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Multithreading/MTTask_SerializeActors.h" - -#include "SaveManager.h" -#include "SaveSlot.h" -#include "SaveSlotData.h" -#include "Serialization/SEArchive.h" - -#include -#include - - -///////////////////////////////////////////////////// -// FMTTask_SerializeActors -void FMTTask_SerializeActors::DoWork() -{ - TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::DoWork); - if (bStoreGameInstance) - { - SerializeGameInstance(); - } - - TArray ActorsToSerialize; - for (int32 I = 0; I < Num; ++I) - { - const AActor* const Actor = (*LevelActors)[StartIndex + I]; - if (Actor && Filter->Stores(Actor)) - { - ActorsToSerialize.Add(Actor); - } - } - - for (const AActor* Actor : ActorsToSerialize) - { - FActorRecord& Record = ActorRecords.AddDefaulted_GetRef(); - SerializeActor(Actor, Record); - } -} - -void FMTTask_SerializeActors::SerializeGameInstance() -{ - TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeGameInstance); - if (UGameInstance* GameInstance = World->GetGameInstance()) - { - FObjectRecord Record{GameInstance}; - - // Serialize into Record Data - FMemoryWriter MemoryWriter(Record.Data, true); - FSEArchive Archive(MemoryWriter, false); - GameInstance->Serialize(Archive); - - SlotData->GameInstance = MoveTemp(Record); - } -} - -bool FMTTask_SerializeActors::SerializeActor(const AActor* Actor, FActorRecord& Record) const -{ - TRACE_CPUPROFILER_EVENT_SCOPE(FMTTask_SerializeActors::SerializeActor); - - // Clean the record - Record = {Actor}; - - Record.bHiddenInGame = Actor->IsHidden(); - Record.bIsProcedural = Filter->IsProcedural(Actor); - - if (Filter->StoresTags(Actor)) - { - Record.Tags = Actor->Tags; - } - else - { - // Only save save-tags - for (const auto& Tag : Actor->Tags) - { - if (Filter->IsSaveTag(Tag)) - { - Record.Tags.Add(Tag); - } - } - } - - if (Filter->StoresTransform(Actor)) - { - Record.Transform = Actor->GetTransform(); - - if (Filter->StoresPhysics(Actor)) - { - USceneComponent* const Root = Actor->GetRootComponent(); - if (Root && Root->Mobility == EComponentMobility::Movable) - { - if (auto* const Primitive = Cast(Root)) - { - Record.LinearVelocity = Primitive->GetPhysicsLinearVelocity(); - Record.AngularVelocity = Primitive->GetPhysicsAngularVelocityInRadians(); - } - else - { - Record.LinearVelocity = Root->GetComponentVelocity(); - } - } - } - } - - SerializeActorComponents(Actor, Record, 1); - - TRACE_CPUPROFILER_EVENT_SCOPE(Serialize); - FMemoryWriter MemoryWriter(Record.Data, true); - FSEArchive Archive(MemoryWriter, false); - const_cast(Actor)->Serialize(Archive); - - return true; -} - -void FMTTask_SerializeActors::SerializeActorComponents( - const AActor* Actor, FActorRecord& ActorRecord, int8 Indent /*= 0*/) const -{ - 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 (IsValid(Component) && Filter->Stores(Component)) - { - FComponentRecord ComponentRecord; - ComponentRecord.Name = Component->GetFName(); - ComponentRecord.Class = Component->GetClass(); - - if (Filter->StoresTransform(Component)) - { - const USceneComponent* Scene = CastChecked(Component); - if (Scene->Mobility == EComponentMobility::Movable) - { - ComponentRecord.Transform = Scene->GetRelativeTransform(); - } - } - - if (Filter->StoresTags(Component)) - { - ComponentRecord.Tags = Component->ComponentTags; - } - - if (!Component->GetClass()->IsChildOf()) - { - FMemoryWriter MemoryWriter(ComponentRecord.Data, true); - FSEArchive Archive(MemoryWriter, false); - Component->Serialize(Archive); - } - ActorRecord.ComponentRecords.Add(ComponentRecord); - } - } -} diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 40f5d1d..799a8d8 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -129,12 +129,12 @@ int32 USaveSlot::GetIndex_Implementation() const return OnGetIndex(); } -bool USaveSlot::IsMTSerializationLoad() const +bool USaveSlot::ShouldDeserializeAsync() const { return MultithreadedSerialization == ESEAsyncMode::LoadAsync || MultithreadedSerialization == ESEAsyncMode::SaveAndLoadAsync; } -bool USaveSlot::IsMTSerializationSave() const +bool USaveSlot::ShouldSerializeAsync() const { return MultithreadedSerialization == ESEAsyncMode::SaveAsync || MultithreadedSerialization == ESEAsyncMode::SaveAndLoadAsync; @@ -151,12 +151,12 @@ float USaveSlot::GetMaxFrameMs() const bool USaveSlot::IsFrameSplitLoad() const { - return !IsMTSerializationLoad() && (FrameSplittedSerialization == ESEAsyncMode::LoadAsync || + return !ShouldDeserializeAsync() && (FrameSplittedSerialization == ESEAsyncMode::LoadAsync || FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsFrameSplitSave() const { - return !IsMTSerializationSave() && (FrameSplittedSerialization == ESEAsyncMode::SaveAsync || + return !ShouldSerializeAsync() && (FrameSplittedSerialization == ESEAsyncMode::SaveAsync || FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index a4b9cc5..e0d21b5 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -10,6 +10,104 @@ #include #include +#include + + +void SerializeActorComponents( + const AActor* Actor, FActorRecord& ActorRecord, const FSELevelFilter& Filter) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActorComponents); + + const TSet& Components = Actor->GetComponents(); + for (auto* Component : Components) + { + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActorComponents | Component); + if (IsValid(Component) && Filter.Stores(Component)) + { + FComponentRecord& ComponentRecord = ActorRecord.ComponentRecords.Add_GetRef({Component}); + + if (Filter.StoresTransform(Component)) + { + const USceneComponent* Scene = CastChecked(Component); + if (Scene->Mobility == EComponentMobility::Movable) + { + ComponentRecord.Transform = Scene->GetRelativeTransform(); + } + } + + if (Filter.StoresTags(Component)) + { + ComponentRecord.Tags = Component->ComponentTags; + } + + if (!Component->GetClass()->IsChildOf()) + { + FMemoryWriter MemoryWriter(ComponentRecord.Data, true); + FSEArchive Archive(MemoryWriter, false); + Component->Serialize(Archive); + } + } + } +} + +bool SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor); + + Record = FActorRecord{Actor}; + + Record.bHiddenInGame = Actor->IsHidden(); + Record.bIsProcedural = Filter.IsProcedural(Actor); + + if (Filter.StoresTags(Actor)) + { + Record.Tags = Actor->Tags; + } + else + { + // Only save save-tags + for (const auto& Tag : Actor->Tags) + { + if (Filter.IsSaveTag(Tag)) + { + Record.Tags.Add(Tag); + } + } + } + + if (Filter.StoresTransform(Actor)) + { + Record.Transform = Actor->GetTransform(); + + if (Filter.StoresPhysics(Actor)) + { + USceneComponent* const Root = Actor->GetRootComponent(); + if (Root && Root->Mobility == EComponentMobility::Movable) + { + if (auto* const Primitive = Cast(Root)) + { + Record.LinearVelocity = Primitive->GetPhysicsLinearVelocity(); + Record.AngularVelocity = Primitive->GetPhysicsAngularVelocityInRadians(); + } + else + { + Record.LinearVelocity = Root->GetComponentVelocity(); + } + } + } + } + + if (Filter.StoresAnyComponents()) + { + SerializeActorComponents(Actor, Record, Filter); + } + + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor | Serialize); + FMemoryWriter MemoryWriter(Record.Data, true); + FSEArchive Archive(MemoryWriter, false); + const_cast(Actor)->Serialize(Archive); + return true; +} ///////////////////////////////////////////////////// @@ -160,21 +258,27 @@ void FSEDataTask_Save::SerializeWorld() const TArray& Levels = World->GetStreamingLevels(); PrepareAllLevels(Levels); - // Threads available + 1 (Synchronous Thread) - const int32 NumberOfThreads = FMath::Max(1, FPlatformMisc::NumberOfWorkerThreadsToSpawn() + 1); - const int32 TasksPerLevel = FMath::Max(1, FMath::RoundToInt(float(NumberOfThreads) / (Levels.Num() + 1))); - Tasks.Reserve(NumberOfThreads); + { // Serialization + UGameInstance* GameInstance = World->GetGameInstance(); + if (GameInstance && Slot->bStoreGameInstance) + { + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeGameInstance); + FObjectRecord Record{GameInstance}; + FMemoryWriter MemoryWriter(Record.Data, true); + FSEArchive Archive(MemoryWriter, false); + GameInstance->Serialize(Archive); + SlotData->GameInstance = MoveTemp(Record); + } - SerializeLevelSync(World->GetCurrentLevel(), TasksPerLevel); - for (const ULevelStreaming* Level : Levels) - { - if (Level->IsLevelLoaded()) + SerializeLevel(World->GetCurrentLevel()); + for (const ULevelStreaming* Level : Levels) { - SerializeLevelSync(Level->GetLoadedLevel(), TasksPerLevel, Level); + if (Level->IsLevelLoaded()) + { + SerializeLevel(Level->GetLoadedLevel(), Level); + } } } - - RunScheduledTasks(); } void FSEDataTask_Save::PrepareAllLevels(const TArray& Levels) @@ -199,81 +303,36 @@ void FSEDataTask_Save::PrepareLevel(const ULevel* Level, FLevelRecord& LevelReco LevelRecord.Filter.BakeAllowedClasses(); } -void FSEDataTask_Save::SerializeLevelSync( - const ULevel* Level, int32 AssignedTasks, const ULevelStreaming* StreamingLevel) +void FSEDataTask_Save::SerializeLevel( + const ULevel* Level, const ULevelStreaming* StreamingLevel) { - TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeLevelSync); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeLevel); check(IsValid(Level)); - if (!Slot->IsMTSerializationSave()) - { - AssignedTasks = 1; - } - const FName LevelName = StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level - FLevelRecord* LevelRecord = &SlotData->RootLevel; - if (StreamingLevel) - { - LevelRecord = FindLevelRecord(StreamingLevel); - } - check(LevelRecord); - - // Empty level record before serializing it - LevelRecord->CleanRecords(); + auto& LevelRecord = StreamingLevel? *FindLevelRecord(StreamingLevel) : SlotData->RootLevel; + const FSELevelFilter& Filter = LevelRecord.Filter; - const int32 MinObjectsPerTask = 40; - const int32 ActorCount = Level->Actors.Num(); - const int32 NumBalancedPerTask = FMath::CeilToInt((float) ActorCount / AssignedTasks); - const int32 NumPerTask = FMath::Max(NumBalancedPerTask, MinObjectsPerTask); + LevelRecord.CleanRecords(); // Empty level record before serializing it - // Split all actors between multi-threaded tasks - int32 Index = 0; - while (Index < ActorCount) + TArray ActorsToSerialize; + for (AActor* Actor : Level->Actors) { - const int32 NumRemaining = ActorCount - Index; - const int32 NumToSerialize = FMath::Min(NumRemaining, NumPerTask); - - // First task saves the GameInstance - bool bStoreGameInstance = Index <= 0 && Slot->bStoreGameInstance; - // Add new Task - Tasks.Emplace(FMTTask_SerializeActors{GetWorld(), SlotData, &Level->Actors, Index, NumToSerialize, - bStoreGameInstance, LevelRecord, &LevelRecord->Filter}); - - Index += NumToSerialize; - } -} - -void FSEDataTask_Save::RunScheduledTasks() -{ - TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::RunScheduledTasks); - // Start all serialization tasks - if (Tasks.Num() > 0) - { - for (int32 I = 1; I < Tasks.Num(); ++I) + if (Actor && Filter.Stores(Actor)) { - if (Slot->IsMTSerializationSave()) - Tasks[I].StartBackgroundTask(); - else - Tasks[I].StartSynchronousTask(); + ActorsToSerialize.Add(Actor); } - // First task stores - Tasks[0].StartSynchronousTask(); } - // Wait until all tasks have finished - for (auto& AsyncTask : Tasks) - { - AsyncTask.EnsureCompletion(); - } - // All tasks finished, sync data - for (auto& AsyncTask : Tasks) + LevelRecord.Actors.SetNum(ActorsToSerialize.Num()); + + ParallelFor(ActorsToSerialize.Num(), [&LevelRecord, &ActorsToSerialize, &Filter](int32 i) { - AsyncTask.GetTask().DumpData(); - } - Tasks.Empty(); + SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter); + }, Slot->ShouldSerializeAsync()? EParallelForFlags::None : EParallelForFlags::ForceSingleThread); } void FSEDataTask_Save::SaveFile() diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp index a45e9c5..85e5adf 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp @@ -19,10 +19,7 @@ void FSEDataTask_SaveLevel::OnStart() PrepareLevel(StreamingLevel->GetLoadedLevel(), *LevelRecord); - const int32 NumberOfThreads = FMath::Max(1, FPlatformMisc::NumberOfWorkerThreadsToSpawn()); - SerializeLevelSync(StreamingLevel->GetLoadedLevel(), NumberOfThreads, StreamingLevel); - - RunScheduledTasks(); + SerializeLevel(StreamingLevel->GetLoadedLevel(), StreamingLevel); Finish(true); return; diff --git a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h index 24c928a..cea08f7 100644 --- a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h +++ b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h @@ -4,7 +4,6 @@ #include "SaveFileHelpers.h" #include "SaveManager.h" - #include diff --git a/Source/SaveExtension/Public/Multithreading/MTTask.h b/Source/SaveExtension/Public/Multithreading/MTTask.h deleted file mode 100644 index f480bfa..0000000 --- a/Source/SaveExtension/Public/Multithreading/MTTask.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "ISaveExtension.h" -#include "LevelFilter.h" - -#include -#include -#include -#include - - -///////////////////////////////////////////////////// -// FSlotDataActorsTask -// Async task to serialize actors from a level. -class FMTTask : public FNonAbandonableTask -{ -public: - /** Used only if Sync */ - UWorld* const World = nullptr; - USaveSlotData* SlotData = nullptr; - - - FMTTask(const bool bIsloading, UWorld* World, USaveSlotData* SlotData) - : World(World) - , SlotData(SlotData) - {} -}; diff --git a/Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h b/Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h deleted file mode 100644 index 14568df..0000000 --- a/Source/SaveExtension/Public/Multithreading/MTTask_SerializeActors.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "Multithreading/MTTask.h" -#include "Multithreading/MTTask_SerializeActors.h" -#include "Serialization/LevelRecords.h" -#include "Serialization/Records.h" - -#include -#include -#include -#include - - -class USaveSlotData; - - -/** Called when game has been saved - * @param Slot the saved slot. Null if save failed - */ -DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); - - -///////////////////////////////////////////////////// -// FMTTask_SerializeActors -// Async task to serialize actors from a level. -class FMTTask_SerializeActors : public FMTTask -{ - const TArray* LevelActors; - const int32 StartIndex = 0; - const int32 Num = 0; - const bool bStoreGameInstance = false; - - FLevelRecord* LevelRecord = nullptr; - const FSELevelFilter* Filter = nullptr; - - - FActorRecord LevelScriptRecord; - TArray ActorRecords; - - -public: - 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(LevelRecord) - , Filter(Filter) - { - // No apparent performance benefit - // ActorRecords.Reserve(Num); - } - - void DoWork(); - - /** Called after task has completed to recover resulting information */ - void DumpData() - { - if (LevelScriptRecord.IsValid()) - LevelRecord->LevelScript = LevelScriptRecord; - - // Shrink not needed. Move wont keep reserved space - LevelRecord->Actors.Append(MoveTemp(ActorRecords)); - } - - FORCEINLINE TStatId GetStatId() const - { - RETURN_QUICK_DECLARE_CYCLE_STAT(FMTTask_SerializeActors, STATGROUP_ThreadPoolAsyncTasks); - } - -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; -}; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 1ec8d33..e998f4d 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -227,8 +227,8 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame virtual int32 OnGetIndex() const; public: - bool IsMTSerializationLoad() const; - bool IsMTSerializationSave() const; + bool ShouldDeserializeAsync() const; + bool ShouldSerializeAsync() const; ESEAsyncMode GetFrameSplitSerialization() const; float GetMaxFrameMs() const; diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index 8c3704f..d5d0b01 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -86,6 +86,8 @@ struct FComponentRecord : public FObjectRecord FTransform Transform; + FComponentRecord() : Super() {} + FComponentRecord(const UActorComponent* Component) : Super(Component) {} virtual bool Serialize(FArchive& Ar) override; }; @@ -107,6 +109,5 @@ struct FActorRecord : public FObjectRecord FActorRecord() : Super() {} FActorRecord(const AActor* Actor) : Super(Actor) {} - virtual bool Serialize(FArchive& Ar) override; }; diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 32939cd..16ec732 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -4,7 +4,6 @@ #include "Delegates.h" #include "ISaveExtension.h" -#include "Multithreading/MTTask_SerializeActors.h" #include "Multithreading/SaveFileTask.h" #include "SaveSlotData.h" #include "SEDataTask.h" @@ -41,7 +40,6 @@ struct FSEDataTask_Save : public FSEDataTask /** End Async variables */ /** Begin AsyncTasks */ - TArray> Tasks; FAsyncTask* SaveTask = nullptr; /** End AsyncTasks */ @@ -76,22 +74,11 @@ struct FSEDataTask_Save : public FSEDataTask virtual void OnFinish(bool bSuccess) override; protected: - /** BEGIN Serialization */ /** Serializes all world actors. */ void SerializeWorld(); - void PrepareAllLevels(const TArray& Levels); void PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord); + void SerializeLevel(const ULevel* Level, 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 */ }; From a3942341dd934f6b3c1fb8e0730d99ad9cbe30d6 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 3 Oct 2023 21:03:17 +0200 Subject: [PATCH 10/39] Refactored PreloadAllSlots --- .../Private/Multithreading/LoadSlotsTask.cpp | 67 -------- .../SaveExtension/Private/SaveFileHelpers.cpp | 16 +- Source/SaveExtension/Private/SaveManager.cpp | 144 ++++++++++++------ .../Private/Serialization/SEDataTask_Load.cpp | 4 +- .../Public/Multithreading/Delegates.h | 3 - .../Public/Multithreading/LoadSlotsTask.h | 58 ------- Source/SaveExtension/Public/SaveFileHelpers.h | 4 +- Source/SaveExtension/Public/SaveManager.h | 58 +++---- 8 files changed, 136 insertions(+), 218 deletions(-) delete mode 100644 Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp delete mode 100644 Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h diff --git a/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp deleted file mode 100644 index 0b4802b..0000000 --- a/Source/SaveExtension/Private/Multithreading/LoadSlotsTask.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Multithreading/LoadSlotsTask.h" - -#include "Misc/SlotHelpers.h" -#include "SaveFileHelpers.h" -#include "SaveManager.h" - -#include - - -void FLoadSlotsTask::DoWork() -{ - if (!Manager) - { - return; - } - - TArray FileNames; - const bool bLoadingSingleInfo = !SlotName.IsNone(); - if (bLoadingSingleInfo) - { - FileNames.Add(SlotName.ToString()); - } - else - { - FSlotHelpers::FindSlotFileNames(FileNames); - } - - TArray LoadedFiles; - LoadedFiles.Reserve(FileNames.Num()); - for (const FString& FileName : FileNames) - { - // Load all files - FScopedFileReader Reader(FSaveFileHelpers::GetSlotPath(FileName)); - if (Reader.IsValid()) - { - auto& File = LoadedFiles.AddDefaulted_GetRef(); - File.Read(Reader, true); - } - } - - // For cache friendlyness, we deserialize infos after loading all the files - LoadedSlots.Reserve(LoadedFiles.Num()); - for (const auto& File : LoadedFiles) - { - LoadedSlots.Add(Cast( - FSaveFileHelpers::DeserializeObject(nullptr, File.InfoClassName, Manager, File.InfoBytes) - )); - } - - if (!bLoadingSingleInfo && bSortByRecent) - { - LoadedSlots.Sort([](const USaveSlot& A, const USaveSlot& B) { - return A.Stats.SaveDate > B.Stats.SaveDate; - }); - } -} - -void FLoadSlotsTask::AfterFinish() -{ - for (auto& Slot : LoadedSlots) - { - Slot->ClearInternalFlags(EInternalObjectFlags::Async); - } - Delegate.ExecuteIfBound(LoadedSlots); -} diff --git a/Source/SaveExtension/Private/SaveFileHelpers.cpp b/Source/SaveExtension/Private/SaveFileHelpers.cpp index df0ae3e..e699ac7 100644 --- a/Source/SaveExtension/Private/SaveFileHelpers.cpp +++ b/Source/SaveExtension/Private/SaveFileHelpers.cpp @@ -110,8 +110,8 @@ void FSaveFile::Read(FScopedFileReader& Reader, bool bSkipData) } } - Ar << InfoClassName; - Ar << InfoBytes; + Ar << ClassName; + Ar << Bytes; Ar << DataClassName; if (bSkipData || DataClassName.IsEmpty()) @@ -160,8 +160,8 @@ void FSaveFile::Write(FScopedFileWriter& Writer, bool bCompressData) Ar, static_cast(CustomVersionFormat)); } - Ar << InfoClassName; - Ar << InfoBytes; + Ar << ClassName; + Ar << Bytes; Ar << DataClassName; if (!DataClassName.IsEmpty()) @@ -191,10 +191,10 @@ void FSaveFile::SerializeInfo(USaveSlot* Slot) { TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFile::SerializeInfo); check(Slot); - InfoBytes.Reset(); - InfoClassName = Slot->GetClass()->GetPathName(); + Bytes.Reset(); + ClassName = Slot->GetClass()->GetPathName(); - FMemoryWriter BytesWriter(InfoBytes); + FMemoryWriter BytesWriter(Bytes); FObjectAndNameAsStringProxyArchive Ar(BytesWriter, false); Slot->Serialize(Ar); } @@ -249,7 +249,7 @@ bool FSaveFileHelpers::LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLo { TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeInfo) - Slot = Cast(DeserializeObject(Slot, File.InfoClassName, Outer, File.InfoBytes)); + Slot = Cast(DeserializeObject(Slot, File.ClassName, Outer, File.Bytes)); } if (bLoadData) { diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index e5160e0..1589fe9 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -3,7 +3,6 @@ #include "SaveManager.h" #include "Multithreading/DeleteSlotsTask.h" -#include "Multithreading/LoadSlotsTask.h" #include "SaveFileHelpers.h" #include "SaveSettings.h" #include "Serialization/SEDataTask_LoadLevel.h" @@ -22,8 +21,24 @@ #include #include #include +#include +UE::Tasks::FPipe Pipe{ TEXT("SaveExtensionPipe") }; + +// From SaveGameSystem.cpp +void OnAsyncComplete(TFunction Callback) +{ + // NB. Using Ticker because AsyncTask may run during async package loading which may not be suitable for save data + FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda( + [Callback = MoveTemp(Callback)](float) -> bool + { + Callback(); + return false; + } + )); +} + // BEGIN Async Actions class FSELoadSlotDataAction : public FPendingLatentAction @@ -96,8 +111,7 @@ class FDeleteSlotsAction : public FPendingLatentAction #endif }; - -class FSELoadInfosAction : public FPendingLatentAction +class FSEPreloadSlotsAction : public FPendingLatentAction { public: TArray& Slots; @@ -106,7 +120,7 @@ class FSELoadInfosAction : public FPendingLatentAction int32 OutputLink; FWeakObjectPtr CallbackTarget; - FSELoadInfosAction(USaveManager* Manager, const bool bSortByRecent, TArray& OutSlots, + FSEPreloadSlotsAction(USaveManager* Manager, const bool bSortByRecent, TArray& OutSlots, ESEContinue& OutResult, const FLatentActionInfo& LatentInfo) : Slots(OutSlots) , Result(OutResult) @@ -115,11 +129,10 @@ class FSELoadInfosAction : public FPendingLatentAction , CallbackTarget(LatentInfo.CallbackTarget) { Result = ESEContinue::InProgress; - Manager->FindAllSlots( - bSortByRecent, FOnSlotsLoaded::CreateLambda([this](const TArray& Results) { - Slots = Results; - Result = ESEContinue::Continue; - })); + Manager->PreloadAllSlots([this](const TArray& InSlots) { + Slots = InSlots; + Result = ESEContinue::Continue; + }, bSortByRecent); } virtual void UpdateOperation(FLatentResponse& Response) override { @@ -267,26 +280,64 @@ bool USaveManager::DeleteSlot(FName SlotName) return bSuccess; } -void USaveManager::FindAllSlots(bool bSortByRecent, FOnSlotsLoaded Delegate) +void USaveManager::PreloadAllSlots(FSEOnSlotsPreloaded Callback, bool bSortByRecent) { - MTTasks.CreateTask(this, bSortByRecent, MoveTemp(Delegate)) - .OnFinished([](auto& Task) { - Task->AfterFinish(); - }) - .StartBackgroundTask(); + // Load slots form a background thread + Pipe.Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() + { + TArray Slots; + PreloadAllSlotsSync(Slots, bSortByRecent); + + for (auto& Slot : Slots) + { + Slot->ClearInternalFlags(EInternalObjectFlags::Async); + } + + if (Callback) + { + OnAsyncComplete([Slots = MoveTemp(Slots), Callback]() + { + Callback(Slots); + }); + } + }); } -void USaveManager::FindAllSlotsSync(bool bSortByRecent, TArray& Slots) +void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRecent) { - auto Delegate = FOnSlotsLoaded::CreateLambda([&Slots](const TArray& FoundSlots) { - Slots = FoundSlots; - }); - MTTasks.CreateTask(this, bSortByRecent, Delegate) - .OnFinished([](auto& Task) { - Task->AfterFinish(); - }) - .StartSynchronousTask(); - MTTasks.Tick(); + TArray FileNames; + FSlotHelpers::FindSlotFileNames(FileNames); + + TArray LoadedFiles; + LoadedFiles.Reserve(FileNames.Num()); + for (const FString& FileName : FileNames) + { + // Load all files + FScopedFileReader Reader(FSaveFileHelpers::GetSlotPath(FileName)); + if (Reader.IsValid()) + { + LoadedFiles.AddDefaulted_GetRef() + .Read(Reader, true); + } + } + + Slots.Reserve(Slots.Num() + LoadedFiles.Num()); + for (const auto& File : LoadedFiles) + { + auto* Slot = Cast( + FSaveFileHelpers::DeserializeObject(nullptr, File.ClassName, this, File.Bytes)); + if (Slot) + { + Slots.Add(Slot); + } + } + + if (bSortByRecent) + { + Slots.Sort([](const USaveSlot& A, const USaveSlot& B) { + return A.Stats.SaveDate > B.Stats.SaveDate; + }); + } } void USaveManager::DeleteAllSlots(FOnSlotsDeleted Delegate) @@ -332,17 +383,17 @@ void USaveManager::BPLoadSlotByName(FName SlotName, ESEContinueOrFail& Result, s Result = ESEContinueOrFail::Failed; } -void USaveManager::BPFindAllSlots(const bool bSortByRecent, TArray& SaveInfos, +void USaveManager::BPPreloadAllSlots(const bool bSortByRecent, TArray& SaveInfos, 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 FSELoadInfosAction(this, bSortByRecent, SaveInfos, Result, LatentInfo)); + new FSEPreloadSlotsAction(this, bSortByRecent, SaveInfos, Result, LatentInfo)); } } } @@ -361,6 +412,14 @@ void USaveManager::BPDeleteAllSlots(ESEContinue& Result, struct FLatentActionInf } } +USaveSlot* USaveManager::PreloadSlot(FName SlotName) +{ + USaveSlot* Slot = nullptr; + const FString NameStr = SlotName.ToString(); + FSaveFileHelpers::LoadFile(NameStr, Slot, true, this); + return Slot; +} + bool USaveManager::IsSlotSaved(FName SlotName) const { return FSaveFileHelpers::FileExists(SlotName.ToString()); @@ -377,9 +436,15 @@ bool USaveManager::CanLoadOrSave() return IsValid(GetWorld()); } +void USaveManager::SetActiveSlot(USaveSlot* NewSlot) +{ + ActiveSlot = NewInfo; + // TODO: Ensure data is not null here +} + void USaveManager::AssureActiveSlot(TSubclassOf ActiveSlotClass, bool bForced) { - if (IsInSlot() && !bForced) + if (HasActiveSlot() && !bForced) return; if (!ActiveSlotClass) @@ -390,7 +455,7 @@ void USaveManager::AssureActiveSlot(TSubclassOf ActiveSlotClass, bool ActiveSlotClass = USaveSlot::StaticClass(); } } - ActiveSlot = NewObject(this, ActiveSlotClass); + SetActiveSlot(NewObject(this, ActiveSlotClass)); } void USaveManager::UpdateLevelStreamings() @@ -430,25 +495,6 @@ void USaveManager::DeserializeStreamingLevel(ULevelStreaming* LevelStreaming) CreateTask().Setup(LevelStreaming).Start(); } -USaveSlot* USaveManager::LoadInfo(FName SlotName) -{ - if (SlotName.IsNone()) - { - SELog(ActiveSlot, "Invalid Slot. Cant go under 0 or exceed MaxSlots", true); - return nullptr; - } - - 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; -} - void USaveManager::FinishTask(FSEDataTask* Task) { Tasks.RemoveAll([Task](auto& TaskPtr) { return TaskPtr.Get() == Task; }); diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 7ff3ec2..91aaf3a 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -29,7 +29,7 @@ void FSEDataTask_Load::OnStart() { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::OnStart); - Slot = Manager->LoadInfo(SlotName); + Slot = Manager->PreloadSlot(SlotName); SELog(Slot, "Loading from Slot " + SlotName.ToString()); if (!Slot) { @@ -156,7 +156,7 @@ void FSEDataTask_Load::StartDeserialization() Slot->Stats.LoadDate = FDateTime::Now(); // Apply current Info if succeeded - Manager->AssignActiveSlot(Slot); + Manager->SetActiveSlot(Slot); Manager->OnLoadBegan(); diff --git a/Source/SaveExtension/Public/Multithreading/Delegates.h b/Source/SaveExtension/Public/Multithreading/Delegates.h index 7870bca..d6617c7 100644 --- a/Source/SaveExtension/Public/Multithreading/Delegates.h +++ b/Source/SaveExtension/Public/Multithreading/Delegates.h @@ -4,8 +4,5 @@ #include - -DECLARE_DELEGATE_OneParam(FOnSlotsLoaded, const TArray&); - // @param Amount of slots removed DECLARE_DELEGATE(FOnSlotsDeleted); diff --git a/Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h b/Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h deleted file mode 100644 index 4a709c9..0000000 --- a/Source/SaveExtension/Public/Multithreading/LoadSlotsTask.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "Multithreading/Delegates.h" -#include "SaveFileHelpers.h" -#include "SaveSlot.h" - -#include - - -class USaveManager; - - -/** - * FLoadSlotsTask - * Async task to load one or many slot infos - */ -class FLoadSlotsTask : 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; - - FOnSlotsLoaded Delegate; - - -public: - /** All infos Constructor */ - explicit FLoadSlotsTask(const USaveManager* Manager, bool bInSortByRecent, const FOnSlotsLoaded& Delegate) - : Manager(Manager) - , bSortByRecent(bInSortByRecent) - , Delegate(Delegate) - {} - - /** One info Constructor */ - explicit FLoadSlotsTask(USaveManager* Manager, FName SlotName) : Manager(Manager), SlotName(SlotName) {} - - void DoWork(); - - /** Called after the task has finished */ - void AfterFinish(); - - const TArray& GetLoadedSlots() const - { - return LoadedSlots; - } - - FORCEINLINE TStatId GetStatId() const - { - RETURN_QUICK_DECLARE_CYCLE_STAT(FLoadAllSlotsTask, STATGROUP_ThreadPoolAsyncTasks); - } -}; diff --git a/Source/SaveExtension/Public/SaveFileHelpers.h b/Source/SaveExtension/Public/SaveFileHelpers.h index 53fff6d..d553fd1 100644 --- a/Source/SaveExtension/Public/SaveFileHelpers.h +++ b/Source/SaveExtension/Public/SaveFileHelpers.h @@ -81,8 +81,8 @@ struct FSaveFile int32 CustomVersionFormat = int32(ECustomVersionSerializationFormat::Unknown); FCustomVersionContainer CustomVersions; - FString InfoClassName; - TArray InfoBytes; + FString ClassName; + TArray Bytes; FString DataClassName; bool bIsDataCompressed = false; diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 672eaa9..cd3bfd0 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -27,7 +27,7 @@ struct FLatentActionInfo; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, Slot); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, Slot); - +using FSEOnSlotsPreloaded = TFunction& Slots)>; UENUM() enum class ESEContinue : uint8 @@ -128,7 +128,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Save the currently loaded Slot */ - bool SaveCurrentSlot( + bool SaveActiveSlot( bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); @@ -139,18 +139,25 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi bool LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded = {}); /** Reload the currently loaded slot if any */ - bool ReloadCurrentSlot(FOnGameLoaded OnLoaded = {}) + bool ReloadActiveSlot(FOnGameLoaded OnLoaded = {}) { return LoadSlot(ActiveSlot, MoveTemp(OnLoaded)); } /** - * Find all saved games and return their Slots + * Find all saved slots and preload them, without loading their data + * @param Slots preloaded from on disk * @param bSortByRecent Should slots be ordered by save date? - * @param SaveInfos All saved games found on disk */ - void FindAllSlots(bool bSortByRecent, FOnSlotsLoaded Delegate); - void FindAllSlotsSync(bool bSortByRecent, TArray& Slots); + void PreloadAllSlots(FSEOnSlotsPreloaded Callback, bool bSortByRecent = false); + + /** + * Find all saved slots and preload them asynchronously, without loading their data + * Performance: Interacts with disk, can be slow + * @param Slots preloaded from on disk + * @param bSortByRecent Should slots be ordered by save date? + */ + void PreloadAllSlotsSync(TArray& Slots, bool bSortByRecent = false); /** Delete a saved game on an specified slot name * Performance: Interacts with disk, can be slow @@ -184,9 +191,9 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Save the currently loaded Slot */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Saving", - meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Current Slot", Latent, + meta = (AdvancedDisplay = "bScreenshot, Size", DisplayName = "Save Active Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPSaveCurrentSlot(bool bScreenshot, + void BPSaveActiveSlot(bool bScreenshot, UPARAM(meta = (EditCondition = bScreenshot)) const FScreenshotSize Size, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { @@ -207,22 +214,22 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Reload the currently loaded slot if any */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Reload Current Slot", Latent, LatentInfo = "LatentInfo", + meta = (DisplayName = "Reload Active Slot", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) - void BPReloadCurrentSlot(ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) + void BPReloadActiveSlot(ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) { BPLoadSlot(ActiveSlot, Result, MoveTemp(LatentInfo)); } /** - * Find all saved games and return their Slots + * Find all saved slots and preload them, without loading their data + * @param Slots preloaded from on disk * @param bSortByRecent Should slots be ordered by save date? - * @param SaveInfos All saved games found on disk */ UFUNCTION(BlueprintCallable, Category = "SaveExtension", meta = (Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", - DisplayName = "Find All Slots")) - void BPFindAllSlots(const bool bSortByRecent, TArray& Slots, ESEContinue& Result, + DisplayName = "Preload All Slots")) + void BPPreloadAllSlots(const bool bSortByRecent, TArray& Slots, ESEContinue& Result, struct FLatentActionInfo LatentInfo); /** Delete all saved slots from disk, loaded or not */ @@ -252,10 +259,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi } UFUNCTION(BlueprintCallable, Category = "SaveExtension|Slots") - FORCEINLINE USaveSlot* GetSlot(FName SlotName) - { - return LoadInfo(SlotName); - } + USaveSlot* PreloadSlot(FName SlotName); /** Check if an slot exists on disk * @return true if the slot exists @@ -268,19 +272,16 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi * @return true if currently playing in a saved slot */ UFUNCTION(BlueprintPure, Category = "SaveExtension|Slots") - FORCEINLINE bool IsInSlot() const + FORCEINLINE bool HasActiveSlot() const { return ActiveSlot != nullptr; } - void AssureActiveSlot(TSubclassOf ActiveSlotClass = {}, bool bForced = false); - - void AssignActiveSlot(USaveSlot* NewInfo) - { - ActiveSlot = NewInfo; - } + // Assigns a new active slot. If this slot is preloaded, empty data is assigned to it. + // This does not load the game! + void SetActiveSlot(USaveSlot* NewSlot); - USaveSlot* LoadInfo(FName FileName); + void AssureActiveSlot(TSubclassOf ActiveSlotClass = {}, bool bForced = false); protected: bool CanLoadOrSave(); @@ -397,8 +398,7 @@ inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, BPSaveSlotByName(Slot->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } -/** Save the currently loaded Slot */ -inline bool USaveManager::SaveCurrentSlot(bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) +inline bool USaveManager::SaveActiveSlot(bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) { return SaveSlot(ActiveSlot, true, bScreenshot, Size, OnSaved); } From 3e41ae60ba8b2a930e44668bedd3baf5dfdf529b Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 3 Oct 2023 21:31:45 +0200 Subject: [PATCH 11/39] Refactored Deleting Slots --- .../Multithreading/DeleteSlotsTask.cpp | 43 ------- Source/SaveExtension/Private/SaveManager.cpp | 100 ++++++++------- .../Public/Multithreading/Delegates.h | 8 -- .../Public/Multithreading/DeleteSlotsTask.h | 41 ------- .../Public/Multithreading/ScopedTaskManager.h | 114 ------------------ Source/SaveExtension/Public/SaveManager.h | 38 +++--- Source/Test/Private/Files.spec.cpp | 4 +- Source/Test/Private/GameInstanceSpec.cpp | 4 +- Source/Test/Private/SavingSpec.cpp | 4 +- 9 files changed, 86 insertions(+), 270 deletions(-) delete mode 100644 Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp delete mode 100644 Source/SaveExtension/Public/Multithreading/Delegates.h delete mode 100644 Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h delete mode 100644 Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h diff --git a/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp b/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp deleted file mode 100644 index 41b2ade..0000000 --- a/Source/SaveExtension/Private/Multithreading/DeleteSlotsTask.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Multithreading/DeleteSlotsTask.h" - -#include "HAL/FileManager.h" -#include "Misc/SlotHelpers.h" -#include "SaveFileHelpers.h" -#include "SaveManager.h" - -#include - - -FDeleteSlotsTask::FDeleteSlotsTask(const USaveManager* InManager, FName SlotName) : Manager(InManager) -{ - check(Manager); - if (!SlotName.IsNone()) - { - SpecificSlotName = SlotName.ToString(); - } -} - -void FDeleteSlotsTask::DoWork() -{ - if (!SpecificSlotName.IsEmpty()) - { - // Delete a single slot by id - const FString ScreenshotPath = FSaveFileHelpers::GetThumbnailPath(SpecificSlotName); - bool bIsDeleteSlotSuccess = FSaveFileHelpers::DeleteFile(SpecificSlotName); - bool bIsDeleteScreenshotSuccess = IFileManager::Get().Delete(*ScreenshotPath, true); - bSuccess = bIsDeleteSlotSuccess || bIsDeleteScreenshotSuccess; - } - else - { - TArray FoundSlots; - FSlotHelpers::FindSlotFileNames(FoundSlots); - - for (const FString& File : FoundSlots) - { - FSaveFileHelpers::DeleteFile(File); - } - bSuccess = true; - } -} diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 1589fe9..6e2914e 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -2,7 +2,6 @@ #include "SaveManager.h" -#include "Multithreading/DeleteSlotsTask.h" #include "SaveFileHelpers.h" #include "SaveSettings.h" #include "Serialization/SEDataTask_LoadLevel.h" @@ -24,7 +23,7 @@ #include -UE::Tasks::FPipe Pipe{ TEXT("SaveExtensionPipe") }; +UE::Tasks::FPipe BackendPipe{ TEXT("SaveExtensionPipe") }; // From SaveGameSystem.cpp void OnAsyncComplete(TFunction Callback) @@ -79,7 +78,7 @@ class FSELoadSlotDataAction : public FPendingLatentAction }; -class FDeleteSlotsAction : public FPendingLatentAction +class FDeleteAllSlotsAction : public FPendingLatentAction { public: ESEContinue& Result; @@ -87,16 +86,16 @@ class FDeleteSlotsAction : public FPendingLatentAction int32 OutputLink; FWeakObjectPtr CallbackTarget; - FDeleteSlotsAction(USaveManager* Manager, ESEContinue& OutResult, const FLatentActionInfo& LatentInfo) + FDeleteAllSlotsAction(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]() { + Manager->DeleteAllSlots([this](int32 Count) { Result = ESEContinue::Continue; - })); + }); } void UpdateOperation(FLatentResponse& Response) override { @@ -189,7 +188,7 @@ class FSaveGameAction : public FPendingLatentAction // END Async Actions -USaveManager::USaveManager() : Super(), MTTasks{} {} +USaveManager::USaveManager() : Super() {} void USaveManager::Initialize(FSubsystemCollectionBase& Collection) { @@ -203,7 +202,7 @@ void USaveManager::Initialize(FSubsystemCollectionBase& Collection) AssureActiveSlot(); if (ActiveSlot && ActiveSlot->bLoadOnStart) { - ReloadCurrentSlot(); + ReloadActiveSlot(); } UpdateLevelStreamings(); @@ -213,10 +212,10 @@ void USaveManager::Deinitialize() { Super::Deinitialize(); - MTTasks.CancelAll(); + BackendPipe.WaitUntilEmpty(); if (GetActiveSlot()->bSaveOnClose) - SaveCurrentSlot(); + SaveActiveSlot(); FCoreUObjectDelegates::PreLoadMap.RemoveAll(this); FCoreUObjectDelegates::PostLoadMapWithWorld.RemoveAll(this); @@ -263,27 +262,9 @@ bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) return Task.IsSucceeded() || Task.IsScheduled(); } -bool USaveManager::DeleteSlot(FName SlotName) -{ - if (SlotName.IsNone()) - { - return false; - } - - bool bSuccess = false; - MTTasks.CreateTask(this, SlotName) - .OnFinished([&bSuccess](auto& Task) mutable { - bSuccess = Task->bSuccess; - }) - .StartSynchronousTask(); - MTTasks.Tick(); - return bSuccess; -} - -void USaveManager::PreloadAllSlots(FSEOnSlotsPreloaded Callback, bool bSortByRecent) +void USaveManager::PreloadAllSlots(FSEOnAllSlotsPreloaded Callback, bool bSortByRecent) { - // Load slots form a background thread - Pipe.Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() + BackendPipe.Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() { TArray Slots; PreloadAllSlotsSync(Slots, bSortByRecent); @@ -340,13 +321,52 @@ void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRe } } -void USaveManager::DeleteAllSlots(FOnSlotsDeleted Delegate) +bool USaveManager::DeleteSlotByNameSync(FName SlotName) +{ + const FString NameStr = SlotName.ToString(); + const FString ScreenshotPath = FSaveFileHelpers::GetThumbnailPath(NameStr); + bool bIsDeleteSlotSuccess = FSaveFileHelpers::DeleteFile(NameStr); + bool bIsDeleteScreenshotSuccess = IFileManager::Get().Delete(*ScreenshotPath, true); + return bIsDeleteSlotSuccess || bIsDeleteScreenshotSuccess; +} + +void USaveManager::DeleteSlotByName(FName SlotName) +{ + BackendPipe.Launch(UE_SOURCE_LOCATION, [this, SlotName]() + { + DeleteSlotByNameSync(SlotName); + }); +} + +int32 USaveManager::DeleteAllSlotsSync() +{ + TArray FoundSlots; + FSlotHelpers::FindSlotFileNames(FoundSlots); + + int32 Count = 0; + for (const FString& SlotName : FoundSlots) + { + const FString ScreenshotPath = FSaveFileHelpers::GetThumbnailPath(SlotName); + bool bIsDeleteSlotSuccess = FSaveFileHelpers::DeleteFile(SlotName); + bool bIsDeleteScreenshotSuccess = IFileManager::Get().Delete(*ScreenshotPath, true); + Count += bIsDeleteSlotSuccess || bIsDeleteScreenshotSuccess; + } + return Count; +} + +void USaveManager::DeleteAllSlots(FSEOnAllSlotsDeleted Callback) { - MTTasks.CreateTask(this) - .OnFinished([Delegate](auto& Task) { - Delegate.ExecuteIfBound(); - }) - .StartBackgroundTask(); + BackendPipe.Launch(UE_SOURCE_LOCATION, [this, Callback]() + { + const int32 Count = DeleteAllSlotsSync(); + if (Callback) + { + OnAsyncComplete([Count, Callback]() + { + Callback(Count); + }); + } + }); } void USaveManager::BPSaveSlotByName(FName SlotName, bool bScreenshot, const FScreenshotSize Size, @@ -403,11 +423,11 @@ void USaveManager::BPDeleteAllSlots(ESEContinue& Result, struct FLatentActionInf 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 FDeleteSlotsAction(this, Result, LatentInfo)); + LatentInfo.CallbackTarget, LatentInfo.UUID, new FDeleteAllSlotsAction(this, Result, LatentInfo)); } } } @@ -438,7 +458,7 @@ bool USaveManager::CanLoadOrSave() void USaveManager::SetActiveSlot(USaveSlot* NewSlot) { - ActiveSlot = NewInfo; + ActiveSlot = NewSlot; // TODO: Ensure data is not null here } @@ -522,8 +542,6 @@ void USaveManager::Tick(float DeltaTime) Task->Tick(DeltaTime); } } - - MTTasks.Tick(); } void USaveManager::SubscribeForEvents(const TScriptInterface& Interface) diff --git a/Source/SaveExtension/Public/Multithreading/Delegates.h b/Source/SaveExtension/Public/Multithreading/Delegates.h deleted file mode 100644 index d6617c7..0000000 --- a/Source/SaveExtension/Public/Multithreading/Delegates.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include - -// @param Amount of slots removed -DECLARE_DELEGATE(FOnSlotsDeleted); diff --git a/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h b/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h deleted file mode 100644 index b3ee532..0000000 --- a/Source/SaveExtension/Public/Multithreading/DeleteSlotsTask.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "Multithreading/Delegates.h" -#include "SaveFileHelpers.h" -#include "SaveSlot.h" - -#include -#include - - - -class USaveManager; - -/** - * FDeleteSlotsTask - * Async task to remove an specific or all slots - */ -class FDeleteSlotsTask : public FNonAbandonableTask -{ -protected: - const USaveManager* const Manager = nullptr; - FString SpecificSlotName; - -public: - bool bSuccess = false; - - /** All infos Constructor */ - explicit FDeleteSlotsTask(const USaveManager* InManager, FName SlotName = {}); - - void DoWork(); - - FORCEINLINE TStatId GetStatId() const - { - RETURN_QUICK_DECLARE_CYCLE_STAT(FDeleteSlotsTask, STATGROUP_ThreadPoolAsyncTasks); - } - -private: - USaveSlot* LoadInfoFromFile(const FString Name) const; -}; diff --git a/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h b/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h deleted file mode 100644 index 6a47800..0000000 --- a/Source/SaveExtension/Public/Multithreading/ScopedTaskManager.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "Misc/TypeTraits.h" - -#include - -class ITaskHolder -{ -public: - virtual bool Tick() = 0; - virtual void Cancel(bool bFinishSynchronously) = 0; - virtual ~ITaskHolder() {} -}; - -template -class FTaskHolder : public FAsyncTask, public ITaskHolder -{ - using Super = FAsyncTask; - -public: - DECLARE_EVENT_OneParam(FTaskHolder, FFinishedEvent, FTaskHolder&); - - bool bNotified = false; - FFinishedEvent _OnFinished; - - FTaskHolder() : ITaskHolder(), Super() {} - virtual ~FTaskHolder() {} - - template - FTaskHolder(ArgTypes&&... CtrArgs) : Super(Forward(CtrArgs)...) - , ITaskHolder() - {} - - auto& OnFinished(TFunction&)> Delegate) - { - _OnFinished.AddLambda(Delegate); - return *this; - } - - virtual bool Tick() override - { - if (Super::IsDone()) - { - TryNotifyFinish(); - return true; - } - return false; - } - - virtual void Cancel(bool bFinishSynchronously) override - { - if (!Super::IsIdle()) - { - Super::EnsureCompletion(bFinishSynchronously); - TryNotifyFinish(); - } - else if (Super::IsDone()) - { - TryNotifyFinish(); - } - } - - TaskType* operator->() - { - return &Super::GetTask(); - } - -private: - void TryNotifyFinish() - { - if (!bNotified) - { - _OnFinished.Broadcast(*this); - bNotified = true; - } - } -}; - -/** Manages the lifetime of many multi-threaded tasks */ -class FScopedTaskList -{ - TArray> Tasks; - -public: - FScopedTaskList() {} - - template - inline FTaskHolder& CreateTask(ArgTypes&&... CtrArgs) - { - auto NewTask = MakeUnique>(Forward(CtrArgs)...); - auto* TaskPtr = NewTask.Get(); - Tasks.Add(MoveTemp(NewTask)); - return *TaskPtr; - } - - void Tick() - { - // Tick all running tasks and remove the ones that finished - Tasks.RemoveAllSwap([](auto& Task) { - return Task->Tick(); - }); - } - - void CancelAll() - { - for (auto& Task : Tasks) - { - Task->Cancel(true); - } - Tasks.Empty(); - } -}; diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index cd3bfd0..76288cb 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -4,8 +4,6 @@ #include "Delegates.h" #include "LevelStreamingNotifier.h" -#include "Multithreading/Delegates.h" -#include "Multithreading/ScopedTaskManager.h" #include "SaveExtensionInterface.h" #include "SaveSlot.h" #include "SaveSlotData.h" @@ -24,10 +22,11 @@ struct FLatentActionInfo; - DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, Slot); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, Slot); -using FSEOnSlotsPreloaded = TFunction& Slots)>; +using FSEOnAllSlotsPreloaded = TFunction& Slots)>; +using FSEOnAllSlotsDeleted = TFunction; + UENUM() enum class ESEContinue : uint8 @@ -88,8 +87,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** The game instance to which this save manager is owned. */ TWeakObjectPtr OwningGameInstance; - FScopedTaskList MTTasks; - UPROPERTY(Transient) TArray LevelStreamingNotifiers; @@ -149,8 +146,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi * @param Slots preloaded from on disk * @param bSortByRecent Should slots be ordered by save date? */ - void PreloadAllSlots(FSEOnSlotsPreloaded Callback, bool bSortByRecent = false); - + void PreloadAllSlots(FSEOnAllSlotsPreloaded Callback, bool bSortByRecent = false); /** * Find all saved slots and preload them asynchronously, without loading their data * Performance: Interacts with disk, can be slow @@ -162,10 +158,13 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Delete a saved game on an specified slot name * Performance: Interacts with disk, can be slow */ - bool DeleteSlot(FName SlotName); - - /** Delete all saved slots from disk, loaded or not */ - void DeleteAllSlots(FOnSlotsDeleted Delegate); + bool DeleteSlotByNameSync(FName SlotName); + /** Deletes all saved slots in disk. Does not affect slots in memory. + * Performance: Interacts with disk, can be slow + */ + int32 DeleteAllSlotsSync(); + /** Deletes all saved slots in disk. Does not affect slots in memory. */ + void DeleteAllSlots(FSEOnAllSlotsDeleted Delegate); /** BLUEPRINT ONLY API */ @@ -241,13 +240,18 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** BLUEPRINTS & C++ API */ public: - /** Delete a saved game on an specified slot - * Performance: Interacts with disk, can be slow - */ + /** Delete a saved game on an specified slot name */ UFUNCTION(BlueprintCallable, Category = "SaveExtension") - bool DeleteSlot(USaveSlot* Slot) + void DeleteSlotByName(FName SlotName); + + /** Delete a saved game on an specified slot */ + UFUNCTION(BlueprintCallable, Category = "SaveExtension") + void DeleteSlot(USaveSlot* Slot) { - return Slot ? DeleteSlot(Slot->FileName) : false; + if (Slot) + { + DeleteSlotByName(Slot->FileName); + } } /** Get the currently loaded Slot. If game was never loaded returns a new Slot */ diff --git a/Source/Test/Private/Files.spec.cpp b/Source/Test/Private/Files.spec.cpp index 97da352..6fc1f37 100644 --- a/Source/Test/Private/Files.spec.cpp +++ b/Source/Test/Private/Files.spec.cpp @@ -80,9 +80,9 @@ void FSaveSpec_Files::Define() if (SaveManager) { bFinishTick = false; - SaveManager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() { + SaveManager->DeleteAllSlots([this](int32 Count) { bFinishTick = true; - })); + }); TickWorldUntil(GetMainWorld(), true, [this](float) { return !bFinishTick; }); diff --git a/Source/Test/Private/GameInstanceSpec.cpp b/Source/Test/Private/GameInstanceSpec.cpp index 522fc53..4f064d7 100644 --- a/Source/Test/Private/GameInstanceSpec.cpp +++ b/Source/Test/Private/GameInstanceSpec.cpp @@ -55,9 +55,9 @@ void FSaveSpec_GameInstance::Define() if (SaveManager) { bFinishTick = false; - SaveManager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() { + SaveManager->DeleteAllSlots([this](int32 Count) { bFinishTick = true; - })); + }); TickWorldUntil(GetMainWorld(), true, [this](float) { return !bFinishTick; diff --git a/Source/Test/Private/SavingSpec.cpp b/Source/Test/Private/SavingSpec.cpp index f84a5f3..58413a6 100644 --- a/Source/Test/Private/SavingSpec.cpp +++ b/Source/Test/Private/SavingSpec.cpp @@ -178,9 +178,9 @@ void FSaveSpec_Preset::Define() if (SaveManager) { bFinishTick = false; - SaveManager->DeleteAllSlots(FOnSlotsDeleted::CreateLambda([this]() { + SaveManager->DeleteAllSlots([this](int32 Count) { bFinishTick = true; - })); + }); TickWorldUntil(GetMainWorld(), true, [this](float) { return !bFinishTick; From 6651239b325722d2875bf90d1e903f3a16f3a6d2 Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 4 Oct 2023 02:05:05 +0200 Subject: [PATCH 12/39] Save thumbnail in SaveSlot --- Source/SaveExtension/Private/SaveSlot.cpp | 161 ++++++++---------- .../Private/Serialization/SEArchive.cpp | 2 +- .../Private/Serialization/SEDataTask_Save.cpp | 96 ++++++----- Source/SaveExtension/Public/SaveFileHelpers.h | 2 + Source/SaveExtension/Public/SaveSlot.h | 87 ++++------ .../Public/Serialization/SEArchive.h | 1 - .../Public/Serialization/SEDataTask_Save.h | 5 +- 7 files changed, 161 insertions(+), 193 deletions(-) diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 799a8d8..2de852d 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -19,114 +20,65 @@ void USaveSlot::PostInitProperties() Data = NewObject(this, DataClass, TEXT("SlotData")); } -bool USaveSlot::OnSetIndex(int32 Index) +void USaveSlot::Serialize(FArchive& Ar) { - FileName = FName{FString::FromInt(Index)}; - return true; -} - -int32 USaveSlot::OnGetIndex() const -{ - return FCString::Atoi(*FileName.ToString()); -} - -UTexture2D* USaveSlot::GetThumbnail() const -{ - if (ThumbnailPath.IsEmpty()) - { - return nullptr; - } - - if (CachedThumbnail) - { - return CachedThumbnail; - } + Super::Serialize(Ar); - // Load thumbnail as Texture2D - UTexture2D* Texture{nullptr}; - TArray RawFileData; - if (GEngine && FFileHelper::LoadFileToArray(RawFileData, *ThumbnailPath)) + bool bHasThumbnail = IsValid(Thumbnail); + Ar << bHasThumbnail; + if (bHasThumbnail) { - IImageWrapperModule& ImageWrapperModule = - FModuleManager::LoadModuleChecked(FName("ImageWrapper")); - TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); - if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(RawFileData.GetData(), RawFileData.Num())) + TArray64 ThumbnailData; + if (Ar.IsLoading()) { - TArray64 UncompressedBGRA; - if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) - { - 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(); - Texture->UpdateResource(); - } + Ar << ThumbnailData; + Thumbnail = FImageUtils::ImportBufferAsTexture2D(ThumbnailData); + } + else + { + uint8* MipData = static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY)); + check( MipData != nullptr ); + int64 MipDataSize = Thumbnail->GetPlatformData()->Mips[0].BulkData.GetBulkDataSize(); + + FImageView MipImage(MipData, Thumbnail->GetPlatformData()->SizeX,Thumbnail->GetPlatformData()->SizeY, 1, ERawImageFormat::BGRA8, EGammaSpace::sRGB); + FImageUtils::CompressImage(ThumbnailData, TEXT("PNG"), MipImage); + Thumbnail->GetPlatformData()->Mips[0].BulkData.Unlock(); + Ar << ThumbnailData; } } - const_cast(this)->CachedThumbnail = Texture; - return Texture; } -bool USaveSlot::CaptureThumbnail(const int32 Width /*= 640*/, const int32 Height /*= 360*/) +void USaveSlot::CaptureThumbnail(FSEOnThumbnailCaptured Callback, const int32 Width /*= 640*/, const int32 Height /*= 360*/) { - if (!GEngine || !GEngine->GameViewport || FileName.IsNone()) + if (!GEngine || bCapturingThumbnail) { - return false; + Callback.ExecuteIfBound(false); + return; } - if (auto* Viewport = GEngine->GameViewport->Viewport) + auto* Viewport = GEngine->GameViewport? GEngine->GameViewport->Viewport : nullptr; + if (!Viewport) { - _SetThumbnailPath(FSaveFileHelpers::GetThumbnailPath(FileName.ToString())); - - // TODO: Removal of a thumbnail should be standarized in a function - IFileManager& FM = IFileManager::Get(); - if (ThumbnailPath.Len() > 0 && FM.FileExists(*ThumbnailPath)) - { - FM.Delete(*ThumbnailPath, false, true, true); - } - - FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig(); - HighResScreenshotConfig.SetHDRCapture(false); - // Set Screenshot path - HighResScreenshotConfig.FilenameOverride = ThumbnailPath; - // Set Screenshot Resolution - GScreenshotResolutionX = Width; - GScreenshotResolutionY = Height; - Viewport->TakeHighResScreenShot(); - return true; + Callback.ExecuteIfBound(false); + return; } - return false; -} - - -int32 USaveSlot::GetMaxIndexes() const -{ - return MaxSlots <= 0 ? 16384 : MaxSlots; -} -bool USaveSlot::IsValidIndex(int32 Index) const -{ - return Index >= 0 && Index < GetMaxIndexes(); -} - -void USaveSlot::_SetThumbnailPath(const FString& Path) -{ - if (ThumbnailPath != Path) + FHighResScreenshotConfig& HighResScreenshotConfig = GetHighResScreenshotConfig(); + HighResScreenshotConfig.SetHDRCapture(false); + // Set Screenshot Resolution + GScreenshotResolutionX = Width; + GScreenshotResolutionY = Height; + GEngine->GameViewport->OnScreenshotCaptured().AddUObject(this, &USaveSlot::OnThumbnailCaptured); + bCapturingThumbnail = Viewport->TakeHighResScreenShot(); + if (!bCapturingThumbnail) { - ThumbnailPath = Path; - CachedThumbnail = nullptr; + GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this); + Callback.ExecuteIfBound(false); + } + else + { + CapturedThumbnailDelegate = MoveTemp(Callback); } -} - -bool USaveSlot::SetIndex_Implementation(int32 Index) -{ - return OnSetIndex(Index); -} - -int32 USaveSlot::GetIndex_Implementation() const -{ - return OnGetIndex(); } bool USaveSlot::ShouldDeserializeAsync() const @@ -152,12 +104,12 @@ float USaveSlot::GetMaxFrameMs() const bool USaveSlot::IsFrameSplitLoad() const { return !ShouldDeserializeAsync() && (FrameSplittedSerialization == ESEAsyncMode::LoadAsync || - FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); + FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsFrameSplitSave() const { return !ShouldSerializeAsync() && (FrameSplittedSerialization == ESEAsyncMode::SaveAsync || - FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); + FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } bool USaveSlot::IsMTFilesLoad() const @@ -186,3 +138,26 @@ void USaveSlot::OnGetLevelFilter(bool bIsLoading, FSELevelFilter& OutFilter) con OutFilter.ActorFilter = ActorFilter; OutFilter.ComponentFilter = ComponentFilter; } + +void USaveSlot::OnThumbnailCaptured(int32 InSizeX, int32 InSizeY, const TArray& InImageData) +{ + if (GEngine->GameViewport) + { + GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this); + } + + Thumbnail = UTexture2D::CreateTransient(InSizeX, InSizeY, PF_B8G8R8A8); + Thumbnail->DeferCompression = true; + FColor* TextureData = static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE)); + for(int32 i = 0; i < InImageData.Num(); ++i, ++TextureData) + { + *TextureData = InImageData[i]; + } + Thumbnail->GetPlatformData()->Mips[0].BulkData.Unlock(); + Thumbnail->UpdateResource(); + + + bCapturingThumbnail = false; + CapturedThumbnailDelegate.ExecuteIfBound(true); + CapturedThumbnailDelegate = {}; +} diff --git a/Source/SaveExtension/Private/Serialization/SEArchive.cpp b/Source/SaveExtension/Private/Serialization/SEArchive.cpp index 71eb8dc..aea4249 100644 --- a/Source/SaveExtension/Private/Serialization/SEArchive.cpp +++ b/Source/SaveExtension/Private/Serialization/SEArchive.cpp @@ -44,7 +44,7 @@ FArchive& FSEArchive::operator<<(UObject*& Obj) } else { - if (Obj) + if (Obj && !Obj->IsTemplate() && !Obj->HasAnyFlags(RF_Transient)) { // Serialize the fully qualified object name FString SavedString{Obj->GetPathName()}; diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index e0d21b5..59a772a 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -148,60 +148,68 @@ void FSEDataTask_Save::OnStart() } } - if (bSave) + if (!bSave) { - const UWorld* World = GetWorld(); + Finish(false); + return; + } + + const UWorld* World = GetWorld(); - Manager->OnSaveBegan(); + Manager->OnSaveBegan(); - Slot = Manager->GetActiveSlot(); - SlotData = Slot->GetData(); - check(SlotData->GetClass() == Slot->DataClass); - SlotData->CleanRecords(true); + Slot = Manager->GetActiveSlot(); + SlotData = Slot->GetData(); + check(SlotData->GetClass() == Slot->DataClass); + SlotData->CleanRecords(true); + + check(Slot && SlotData); - check(Slot && SlotData); + const bool bSlotExisted = Slot->FileName == SlotName; + Slot->FileName = SlotName; - const bool bSlotExisted = Slot->FileName == SlotName; - Slot->FileName = SlotName; + if (bCaptureThumbnail) + { + bWaitingThumbnail = true; + Slot->CaptureThumbnail(FSEOnThumbnailCaptured::CreateLambda([this](bool bSuccess) { + bWaitingThumbnail = false; + }), Width, Height); + } + + // Time stats + { + FSaveSlotStats& Stats = Slot->Stats; + Stats.SaveDate = FDateTime::Now(); - if (bSaveThumbnail) + // If this info has been loaded ever + const bool bWasLoaded = Stats.LoadDate.GetTicks() > 0; + if (bWasLoaded) { - Slot->CaptureThumbnail(Width, Height); + // Now - Loaded + const FTimespan SessionTime = Stats.SaveDate - Stats.LoadDate; + Stats.PlayedTime += SessionTime; + Stats.SlotPlayedTime = bSlotExisted ? (Stats.SlotPlayedTime + SessionTime) : SessionTime; } - - // Time stats + else { - FSaveSlotStats& Stats = Slot->Stats; - Stats.SaveDate = FDateTime::Now(); + // Slot is new, played time is world seconds + Stats.PlayedTime = FTimespan::FromSeconds(World->TimeSeconds); + Stats.SlotPlayedTime = Stats.PlayedTime; + } - // If this info has been loaded ever - const bool bWasLoaded = Stats.LoadDate.GetTicks() > 0; - if (bWasLoaded) - { - // Now - Loaded - 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 - Stats.PlayedTime = FTimespan::FromSeconds(World->TimeSeconds); - Stats.SlotPlayedTime = Stats.PlayedTime; - } + // Save current game seconds + SlotData->TimeSeconds = World->TimeSeconds; + } - // Save current game seconds - SlotData->TimeSeconds = World->TimeSeconds; - } + // Save Level info + Slot->Map = FName{FSlotHelpers::GetWorldName(World)}; - // Save Level info - Slot->Map = FName{FSlotHelpers::GetWorldName(World)}; + SerializeWorld(); - SerializeWorld(); + if (!bWaitingThumbnail) // Tick will check if thumbnail is not ready + { SaveFile(); - return; } - Finish(false); } void FSEDataTask_Save::Tick(float DeltaTime) @@ -211,9 +219,9 @@ void FSEDataTask_Save::Tick(float DeltaTime) if (SaveTask && SaveTask->IsDone()) { - if (bSaveThumbnail) + if (bCaptureThumbnail) { - if (Slot && Slot->GetThumbnail()) + if (Slot && Slot->Thumbnail) { Finish(true); } @@ -223,6 +231,10 @@ void FSEDataTask_Save::Tick(float DeltaTime) Finish(true); } } + else if (!SaveTask && !bWaitingThumbnail) + { + SaveFile(); + } } void FSEDataTask_Save::OnFinish(bool bSuccess) @@ -349,7 +361,7 @@ void FSEDataTask_Save::SaveFile() { SaveTask->StartSynchronousTask(); - if (!bSaveThumbnail) + if (!bCaptureThumbnail) { Finish(true); } diff --git a/Source/SaveExtension/Public/SaveFileHelpers.h b/Source/SaveExtension/Public/SaveFileHelpers.h index d553fd1..22c29c2 100644 --- a/Source/SaveExtension/Public/SaveFileHelpers.h +++ b/Source/SaveExtension/Public/SaveFileHelpers.h @@ -88,6 +88,8 @@ struct FSaveFile bool bIsDataCompressed = false; TArray DataBytes; + //TArray ThumbnailBytes; + FSaveFile(); diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index e998f4d..3c98b6e 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -13,16 +13,18 @@ struct FSELevelFilter; +DECLARE_DELEGATE_OneParam(FSEOnThumbnailCaptured, bool); + /** * Specifies the behavior while saving or loading */ UENUM() enum class ESEAsyncMode : uint8 { - SaveAndLoadSync, - LoadAsync, - SaveAsync, - SaveAndLoadAsync + SaveAndLoadSync = 0, + LoadAsync = 1, + SaveAsync = 2, + SaveAndLoadAsync = LoadAsync | SaveAsync }; @@ -32,19 +34,19 @@ struct FSaveSlotStats GENERATED_BODY() /** Played time since this saved game was started. Not related to slots, slots can change */ - UPROPERTY(BlueprintReadOnly, Category = Slot) + UPROPERTY(BlueprintReadOnly, Category = SaveSlot) FTimespan PlayedTime = FTimespan::Zero(); /** Played time since this saved game was created */ - UPROPERTY(BlueprintReadOnly, Category = Slot) + UPROPERTY(BlueprintReadOnly, Category = SaveSlot) FTimespan SlotPlayedTime = FTimespan::Zero(); /** Last date at which this slot was saved. */ - UPROPERTY(BlueprintReadOnly, Category = Slot) + UPROPERTY(BlueprintReadOnly, Category = SaveSlot) FDateTime SaveDate = FDateTime::Now(); /** Date at which this slot was loaded. */ - UPROPERTY(BlueprintReadOnly, Transient, Category = Slot) + UPROPERTY(BlueprintReadOnly, Transient, Category = SaveSlot) FDateTime LoadDate; }; @@ -66,10 +68,6 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame 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 = "Settings", meta = (ClampMin = "0")) - int32 MaxSlots = 0; - /** If checked, will attempt to Save Game to first Slot found, timed event. */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") bool bPeriodicSave = false; @@ -153,41 +151,47 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame public: /** Slot where this SaveInfo and its saveData are saved */ - UPROPERTY(SaveGame, BlueprintReadWrite, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) FName FileName = TEXT("Default"); - UPROPERTY(SaveGame, BlueprintReadWrite, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) FText DisplayName; /** Root Level where this Slot was saved */ - UPROPERTY(SaveGame, BlueprintReadOnly, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadOnly, Category = SaveSlot) FName Map; - UPROPERTY(SaveGame, BlueprintReadWrite, Category = Slot) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) FSaveSlotStats Stats; + UPROPERTY(BlueprintReadWrite, Transient) // Saved + TObjectPtr Thumbnail; + protected: - UPROPERTY(SaveGame) - FString ThumbnailPath; - /** Thumbnail gets cached here the first time it is requested */ UPROPERTY(Transient) - TObjectPtr CachedThumbnail; + bool bCapturingThumbnail = false; + FSEOnThumbnailCaptured CapturedThumbnailDelegate; - UPROPERTY(Transient, BlueprintReadOnly, Category = Slot) + UPROPERTY(Transient, BlueprintReadOnly, Category = SaveSlot) TObjectPtr Data; public: void PostInitProperties() override; + void Serialize(FArchive& Ar) override; - /** Returns this slot's thumbnail if any */ - UFUNCTION(BlueprintCallable, Category = Slot) - UTexture2D* GetThumbnail() const; + /** Captures a thumbnail for the current slot + * @return true if thumbnail was requested. Only one can be requested at the same time. + */ + void CaptureThumbnail(FSEOnThumbnailCaptured Callback, const int32 Width = 640, const int32 Height = 360); /** Captures a thumbnail for the current slot */ - bool CaptureThumbnail(const int32 Width = 640, const int32 Height = 360); - + UFUNCTION(BlueprintCallable, Category = SaveSlot, meta = (DisplayName = "Capture Thumbnail")) + void BPCaptureThumbnail(const int32 Width = 640, const int32 Height = 360) + { + CaptureThumbnail({}, Width, Height); + } USaveSlotData* GetData() const { @@ -200,33 +204,6 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame Data = NewData; } - UFUNCTION(BlueprintPure, Category = Slot) - int32 GetMaxIndexes() 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); - - /** Internal Usage. Will be called to remove previous thumbnail */ - FString _GetThumbnailPath() - { - return ThumbnailPath; - } - -public: - UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = Slot) - bool SetIndex(int32 Index); - - UFUNCTION(BlueprintPure, BlueprintNativeEvent, Category = Slot) - int32 GetIndex() const; - -protected: - virtual bool OnSetIndex(int32 Index); - virtual int32 OnGetIndex() const; - -public: bool ShouldDeserializeAsync() const; bool ShouldSerializeAsync() const; @@ -243,10 +220,12 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame bool IsLoadingOrSaving() const; // Called for every level before being saved or loaded - UFUNCTION(BlueprintNativeEvent, Category = Slot) + UFUNCTION(BlueprintNativeEvent, Category = SaveSlot) 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; + + void OnThumbnailCaptured(int32 InSizeX, int32 InSizeY, const TArray& InImageData); }; diff --git a/Source/SaveExtension/Public/Serialization/SEArchive.h b/Source/SaveExtension/Public/Serialization/SEArchive.h index f0d9100..ef7905c 100644 --- a/Source/SaveExtension/Public/Serialization/SEArchive.h +++ b/Source/SaveExtension/Public/Serialization/SEArchive.h @@ -9,7 +9,6 @@ struct FSEArchive : public FObjectAndNameAsStringProxyArchive { public: - FSEArchive(FArchive &InInnerArchive, bool bInLoadIfFindFails) : FObjectAndNameAsStringProxyArchive(InInnerArchive,bInLoadIfFindFails) { diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 16ec732..b9f60cf 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -23,7 +23,7 @@ struct FSEDataTask_Save : public FSEDataTask { bool bOverride = false; - bool bSaveThumbnail = false; + bool bCaptureThumbnail = false; FName SlotName; int32 Width = 0; int32 Height = 0; @@ -43,6 +43,7 @@ struct FSEDataTask_Save : public FSEDataTask FAsyncTask* SaveTask = nullptr; /** End AsyncTasks */ + bool bWaitingThumbnail = false; public: FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) @@ -55,7 +56,7 @@ struct FSEDataTask_Save : public FSEDataTask { SlotName = InSlotName; bOverride = bInOverride; - bSaveThumbnail = bInSaveThumbnail; + bCaptureThumbnail = bInSaveThumbnail; Width = InWidth; Height = InHeight; From ceea8f87df16170a41e3d7f1bbd66858e75ef4da Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 4 Oct 2023 17:23:35 +0200 Subject: [PATCH 13/39] Refactor Save & Load async files --- .../Private/Misc/SlotHelpers.cpp | 2 +- .../Private/Multithreading/LoadFileTask.cpp | 48 ----------- ...{SaveFileHelpers.cpp => SEFileHelpers.cpp} | 79 +++++++++++++------ Source/SaveExtension/Private/SaveManager.cpp | 39 ++++----- Source/SaveExtension/Private/SaveSlot.cpp | 6 +- .../Private/Serialization/SEDataTask_Load.cpp | 40 +++++----- .../Private/Serialization/SEDataTask_Save.cpp | 48 ++++------- .../SaveExtension/Public/Misc/SlotHelpers.h | 2 +- .../Public/Multithreading/LoadFileTask.h | 46 ----------- .../Public/Multithreading/SaveFileTask.h | 36 --------- .../{SaveFileHelpers.h => SEFileHelpers.h} | 16 ++-- Source/SaveExtension/Public/SaveManager.h | 10 +-- Source/SaveExtension/Public/SaveSlot.h | 8 +- .../Public/Serialization/SEDataTask_Load.h | 17 +--- .../Public/Serialization/SEDataTask_Save.h | 5 +- Source/Test/Private/Files.spec.cpp | 13 ++- 16 files changed, 136 insertions(+), 279 deletions(-) delete mode 100644 Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp rename Source/SaveExtension/Private/{SaveFileHelpers.cpp => SEFileHelpers.cpp} (77%) delete mode 100644 Source/SaveExtension/Public/Multithreading/LoadFileTask.h delete mode 100644 Source/SaveExtension/Public/Multithreading/SaveFileTask.h rename Source/SaveExtension/Public/{SaveFileHelpers.h => SEFileHelpers.h} (78%) diff --git a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp index 4b55b98..500c804 100644 --- a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp +++ b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp @@ -10,7 +10,7 @@ void FSlotHelpers::FindSlotFileNames(TArray& FoundSlots) { FFindSlotVisitor Visitor{FoundSlots}; FPlatformFileManager::Get().GetPlatformFile().IterateDirectory( - *FSaveFileHelpers::GetSaveFolder(), Visitor); + *FSEFileHelpers::GetSaveFolder(), Visitor); } bool FSlotHelpers::FFindSlotVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) diff --git a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp b/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp deleted file mode 100644 index f9a4347..0000000 --- a/Source/SaveExtension/Private/Multithreading/LoadFileTask.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Multithreading/LoadFileTask.h" - - -///////////////////////////////////////////////////// -// 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/SaveFileHelpers.cpp b/Source/SaveExtension/Private/SEFileHelpers.cpp similarity index 77% rename from Source/SaveExtension/Private/SaveFileHelpers.cpp rename to Source/SaveExtension/Private/SEFileHelpers.cpp index e699ac7..a49c339 100644 --- a/Source/SaveExtension/Private/SaveFileHelpers.cpp +++ b/Source/SaveExtension/Private/SEFileHelpers.cpp @@ -1,8 +1,7 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "SaveFileHelpers.h" +#include "SEFileHelpers.h" -#include "Multithreading/SaveFileTask.h" #include "Serialization/SEArchive.h" #include "SaveSlot.h" #include "SaveSlotData.h" @@ -14,10 +13,14 @@ #include #include #include +#include static const int SE_SAVEGAME_FILE_TYPE_TAG = 0x0001; // "sAvG" +UE::Tasks::FPipe BackendPipe{ TEXT("SaveExtensionPipe") }; + + struct FSaveGameFileVersion { enum Type @@ -210,14 +213,9 @@ void FSaveFile::SerializeData(USaveSlotData* SlotData) SlotData->Serialize(Ar); } -bool FSaveFileHelpers::SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression) +bool FSEFileHelpers::SaveFileSync(USaveSlot* Slot, FStringView OverrideSlotName, const bool bUseCompression) { - TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFileHelpers::SaveFile); - - if (SlotName.IsEmpty()) - { - return false; - } + TRACE_CPUPROFILER_EVENT_SCOPE(FSEFileHelpers::SaveFileSync); if (!ensureMsgf(Slot, TEXT("Slot object must be valid")) || !ensureMsgf(Slot->GetData(), TEXT("Slot Data object must be valid"))) @@ -225,6 +223,7 @@ bool FSaveFileHelpers::SaveFile(FStringView SlotName, USaveSlot* Slot, const boo return false; } + FString SlotName = OverrideSlotName.IsEmpty()? Slot->Name.ToString() : FString{OverrideSlotName}; FScopedFileWriter FileWriter(GetSlotPath(SlotName)); if (FileWriter.IsValid()) { @@ -237,19 +236,31 @@ bool FSaveFileHelpers::SaveFile(FStringView SlotName, USaveSlot* Slot, const boo return false; } -bool FSaveFileHelpers::LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLoadData, const UObject* Outer) +UE::Tasks::TTask FSEFileHelpers::SaveFile(USaveSlot* Slot, FString OverrideSlotName, const bool bUseCompression) +{ + return BackendPipe.Launch(TEXT("SaveFile"), [Slot, OverrideSlotName, bUseCompression]() { + return SaveFileSync(Slot, OverrideSlotName, bUseCompression); + }); +} + + +USaveSlot* FSEFileHelpers::LoadFileSync(FStringView SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager) { - TRACE_CPUPROFILER_EVENT_SCOPE(FSaveFileHelpers::LoadFile); + TRACE_CPUPROFILER_EVENT_SCOPE(FSEFileHelpers::LoadFileSync); + if (SlotName.IsEmpty() && SlotHint) + { + SlotName = SlotHint->Name.ToString(); + } FScopedFileReader Reader(GetSlotPath(SlotName)); if (Reader.IsValid()) { FSaveFile File{}; File.Read(Reader, !bLoadData); - + USaveSlot* Slot; { TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeInfo) - Slot = Cast(DeserializeObject(Slot, File.ClassName, Outer, File.Bytes)); + Slot = Cast(DeserializeObject(SlotHint, File.ClassName, Manager, File.Bytes)); } if (bLoadData) { @@ -258,38 +269,51 @@ bool FSaveFileHelpers::LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLo DeserializeObject(Slot->GetData(), File.DataClassName, Slot, File.DataBytes)) ); } - return true; + return Slot; } - return false; + return nullptr; } -bool FSaveFileHelpers::DeleteFile(FStringView SlotName) +UE::Tasks::TTask FSEFileHelpers::LoadFile(FString SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager) +{ + return BackendPipe.Launch(TEXT("LoadFile"), [SlotName, SlotHint, bLoadData, Manager]() + { + USaveSlot* Slot = LoadFileSync(SlotName, SlotHint, bLoadData, Manager); + // In case we create the slot from async loading thread + if (Slot) + { + Slot->ClearInternalFlags(EInternalObjectFlags::Async); + if (IsValid(Slot->GetData())) + { + Slot->GetData()->ClearInternalFlags(EInternalObjectFlags::Async); + } + } + return Slot; + }); +} + +bool FSEFileHelpers::DeleteFile(FStringView SlotName) { return IFileManager::Get().Delete(*GetSlotPath(SlotName), true, false, true); } -bool FSaveFileHelpers::FileExists(FStringView SlotName) +bool FSEFileHelpers::FileExists(FStringView SlotName) { return IFileManager::Get().FileSize(*GetSlotPath(SlotName)) >= 0; } -const FString& FSaveFileHelpers::GetSaveFolder() +const FString& FSEFileHelpers::GetSaveFolder() { static const FString Folder = FString::Printf(TEXT("%sSaveGames/"), *FPaths::ProjectSavedDir()); return Folder; } -FString FSaveFileHelpers::GetSlotPath(FStringView SlotName) +FString FSEFileHelpers::GetSlotPath(FStringView SlotName) { return GetSaveFolder() / FString::Printf(TEXT("%s.sav"), SlotName.GetData()); } -FString FSaveFileHelpers::GetThumbnailPath(FStringView SlotName) -{ - return GetSaveFolder() / FString::Printf(TEXT("%s.png"), SlotName.GetData()); -} - -UObject* FSaveFileHelpers::DeserializeObject(UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes) +UObject* FSEFileHelpers::DeserializeObject(UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes) { UObject* Object = Hint; @@ -325,3 +349,8 @@ UObject* FSaveFileHelpers::DeserializeObject(UObject* Hint, FStringView ClassNam Object->Serialize(Ar); return Object; } + +UE::Tasks::FPipe& FSEFileHelpers::GetPipe() +{ + return BackendPipe; +} diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 6e2914e..4fb7d3e 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -2,7 +2,7 @@ #include "SaveManager.h" -#include "SaveFileHelpers.h" +#include "SEFileHelpers.h" #include "SaveSettings.h" #include "Serialization/SEDataTask_LoadLevel.h" #include "Serialization/SEDataTask_SaveLevel.h" @@ -23,8 +23,6 @@ #include -UE::Tasks::FPipe BackendPipe{ TEXT("SaveExtensionPipe") }; - // From SaveGameSystem.cpp void OnAsyncComplete(TFunction Callback) { @@ -212,7 +210,7 @@ void USaveManager::Deinitialize() { Super::Deinitialize(); - BackendPipe.WaitUntilEmpty(); + FSEFileHelpers::GetPipe().WaitUntilEmpty(); if (GetActiveSlot()->bSaveOnClose) SaveActiveSlot(); @@ -264,20 +262,19 @@ bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) void USaveManager::PreloadAllSlots(FSEOnAllSlotsPreloaded Callback, bool bSortByRecent) { - BackendPipe.Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() + FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() { TArray Slots; PreloadAllSlotsSync(Slots, bSortByRecent); - for (auto& Slot : Slots) - { - Slot->ClearInternalFlags(EInternalObjectFlags::Async); - } - if (Callback) { OnAsyncComplete([Slots = MoveTemp(Slots), Callback]() { + for(auto* Slot : Slots) + { + Slot->ClearInternalFlags(EInternalObjectFlags::Async); + } Callback(Slots); }); } @@ -294,7 +291,7 @@ void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRe for (const FString& FileName : FileNames) { // Load all files - FScopedFileReader Reader(FSaveFileHelpers::GetSlotPath(FileName)); + FScopedFileReader Reader(FSEFileHelpers::GetSlotPath(FileName)); if (Reader.IsValid()) { LoadedFiles.AddDefaulted_GetRef() @@ -306,7 +303,7 @@ void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRe for (const auto& File : LoadedFiles) { auto* Slot = Cast( - FSaveFileHelpers::DeserializeObject(nullptr, File.ClassName, this, File.Bytes)); + FSEFileHelpers::DeserializeObject(nullptr, File.ClassName, this, File.Bytes)); if (Slot) { Slots.Add(Slot); @@ -324,15 +321,12 @@ void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRe bool USaveManager::DeleteSlotByNameSync(FName SlotName) { const FString NameStr = SlotName.ToString(); - const FString ScreenshotPath = FSaveFileHelpers::GetThumbnailPath(NameStr); - bool bIsDeleteSlotSuccess = FSaveFileHelpers::DeleteFile(NameStr); - bool bIsDeleteScreenshotSuccess = IFileManager::Get().Delete(*ScreenshotPath, true); - return bIsDeleteSlotSuccess || bIsDeleteScreenshotSuccess; + return FSEFileHelpers::DeleteFile(NameStr); } void USaveManager::DeleteSlotByName(FName SlotName) { - BackendPipe.Launch(UE_SOURCE_LOCATION, [this, SlotName]() + FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, SlotName]() { DeleteSlotByNameSync(SlotName); }); @@ -346,17 +340,14 @@ int32 USaveManager::DeleteAllSlotsSync() int32 Count = 0; for (const FString& SlotName : FoundSlots) { - const FString ScreenshotPath = FSaveFileHelpers::GetThumbnailPath(SlotName); - bool bIsDeleteSlotSuccess = FSaveFileHelpers::DeleteFile(SlotName); - bool bIsDeleteScreenshotSuccess = IFileManager::Get().Delete(*ScreenshotPath, true); - Count += bIsDeleteSlotSuccess || bIsDeleteScreenshotSuccess; + Count += FSEFileHelpers::DeleteFile(SlotName); } return Count; } void USaveManager::DeleteAllSlots(FSEOnAllSlotsDeleted Callback) { - BackendPipe.Launch(UE_SOURCE_LOCATION, [this, Callback]() + FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, Callback]() { const int32 Count = DeleteAllSlotsSync(); if (Callback) @@ -436,13 +427,13 @@ USaveSlot* USaveManager::PreloadSlot(FName SlotName) { USaveSlot* Slot = nullptr; const FString NameStr = SlotName.ToString(); - FSaveFileHelpers::LoadFile(NameStr, Slot, true, this); + Slot = FSEFileHelpers::LoadFileSync(NameStr, nullptr, true, this); return Slot; } bool USaveManager::IsSlotSaved(FName SlotName) const { - return FSaveFileHelpers::FileExists(SlotName.ToString()); + return FSEFileHelpers::FileExists(SlotName.ToString()); } bool USaveManager::CanLoadOrSave() diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 2de852d..4c05142 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -2,7 +2,7 @@ #include "SaveSlot.h" -#include "SaveFileHelpers.h" +#include "SEFileHelpers.h" #include #include @@ -112,12 +112,12 @@ bool USaveSlot::IsFrameSplitSave() const FrameSplittedSerialization == ESEAsyncMode::SaveAndLoadAsync); } -bool USaveSlot::IsMTFilesLoad() const +bool USaveSlot::ShouldLoadFileAsync() const { return MultithreadedFiles == ESEAsyncMode::LoadAsync || MultithreadedFiles == ESEAsyncMode::SaveAndLoadAsync; } -bool USaveSlot::IsMTFilesSave() const +bool USaveSlot::ShouldSaveFileAsync() const { return MultithreadedFiles == ESEAsyncMode::SaveAsync || MultithreadedFiles == ESEAsyncMode::SaveAndLoadAsync; diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 91aaf3a..ace68de 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -18,10 +18,9 @@ FSEDataTask_Load::~FSEDataTask_Load() { - if (LoadDataTask) + if (!LoadFileTask.IsCompleted()) { - LoadDataTask->EnsureCompletion(false); - delete LoadDataTask; + LoadFileTask.Wait(); } } @@ -39,7 +38,7 @@ void FSEDataTask_Load::OnStart() } // We load data while the map opens or GC runs - StartLoadingData(); + StartLoadingFile(); const UWorld* World = GetWorld(); @@ -55,7 +54,7 @@ void FSEDataTask_Load::OnStart() UE_LOG(LogSaveExtension, Warning, TEXT("Slot '%s' was saved in map '%s' but it did not exist while loading. Corrupted save " "file?"), - *Slot->FileName.ToString(), *MapToOpen); + *Slot->Name.ToString(), *MapToOpen); Finish(false); return; } @@ -67,7 +66,7 @@ void FSEDataTask_Load::OnStart() FColor::White, false, 1); return; } - else if (IsDataLoaded()) + else if (CheckFileLoaded()) { StartDeserialization(); } @@ -90,7 +89,7 @@ void FSEDataTask_Load::Tick(float DeltaTime) break; case ELoadDataTaskState::WaitingForData: - if (IsDataLoaded()) + if (CheckFileLoaded()) { StartDeserialization(); } @@ -127,7 +126,7 @@ void FSEDataTask_Load::OnMapLoaded() const FName NewMapName{FSlotHelpers::GetWorldName(World)}; if (NewMapName == Slot->Map) { - if (IsDataLoaded()) + if (CheckFileLoaded()) { StartDeserialization(); } @@ -145,7 +144,6 @@ void FSEDataTask_Load::StartDeserialization() LoadState = ELoadDataTaskState::Deserializing; - SlotData = GetLoadedData(); if (!SlotData) { // Failed to load data @@ -168,23 +166,25 @@ void FSEDataTask_Load::StartDeserialization() DeserializeSync(); } -void FSEDataTask_Load::StartLoadingData() +void FSEDataTask_Load::StartLoadingFile() { - LoadDataTask = new FAsyncTask(Manager, Slot, SlotName.ToString()); - - if (Slot->IsMTFilesLoad()) - LoadDataTask->StartBackgroundTask(); - else - LoadDataTask->StartSynchronousTask(); + LoadFileTask = FSEFileHelpers::LoadFile(SlotName.ToString(), Slot, true, Manager); + if (!Slot->ShouldLoadFileAsync()) + { + LoadFileTask.Wait(); + CheckFileLoaded(); + } } -USaveSlotData* FSEDataTask_Load::GetLoadedData() const +bool FSEDataTask_Load::CheckFileLoaded() { - if (IsDataLoaded()) + if (LoadFileTask.IsCompleted()) { - return LoadDataTask->GetTask().GetData(); + Slot = LoadFileTask.GetResult(); + SlotData = Slot->GetData(); + return true; } - return nullptr; + return false; } void FSEDataTask_Load::BeforeDeserialize() diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 59a772a..3b0a192 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -3,7 +3,7 @@ #include "Serialization/SEDataTask_Save.h" #include "Misc/SlotHelpers.h" -#include "SaveFileHelpers.h" +#include "SEFileHelpers.h" #include "SaveManager.h" #include "SaveSlot.h" #include "SaveSlotData.h" @@ -115,10 +115,9 @@ bool SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFil FSEDataTask_Save::~FSEDataTask_Save() { - if (SaveTask) + if (!SaveFileTask.IsCompleted()) { - SaveTask->EnsureCompletion(false); - delete SaveTask; + SaveFileTask.Wait(); } } @@ -131,13 +130,13 @@ void FSEDataTask_Save::OnStart() const FString SlotNameStr = SlotName.ToString(); // Overriding { - const bool bFileExists = FSaveFileHelpers::FileExists(SlotNameStr); + const bool bFileExists = FSEFileHelpers::FileExists(SlotNameStr); if (bOverride) { // Delete previous save if (bFileExists) { - FSaveFileHelpers::DeleteFile(SlotNameStr); + FSEFileHelpers::DeleteFile(SlotNameStr); } } else @@ -165,8 +164,8 @@ void FSEDataTask_Save::OnStart() check(Slot && SlotData); - const bool bSlotExisted = Slot->FileName == SlotName; - Slot->FileName = SlotName; + const bool bSlotExisted = Slot->Name == SlotName; + Slot->Name = SlotName; if (bCaptureThumbnail) { @@ -217,21 +216,11 @@ void FSEDataTask_Save::Tick(float DeltaTime) TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::Tick); FSEDataTask::Tick(DeltaTime); - if (SaveTask && SaveTask->IsDone()) + if (SaveFileTask.IsValid() && SaveFileTask.IsCompleted()) { - if (bCaptureThumbnail) - { - if (Slot && Slot->Thumbnail) - { - Finish(true); - } - } - else - { - Finish(true); - } + Finish(SaveFileTask.GetResult()); } - else if (!SaveTask && !bWaitingThumbnail) + else if (!SaveFileTask.IsValid() && !bWaitingThumbnail) { SaveFile(); } @@ -350,20 +339,11 @@ void FSEDataTask_Save::SerializeLevel( void FSEDataTask_Save::SaveFile() { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SaveFile); - SaveTask = - new FAsyncTask(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); + SaveFileTask = FSEFileHelpers::SaveFile(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); - if (Slot->IsMTFilesSave()) + if (!Slot->ShouldSaveFileAsync()) { - SaveTask->StartBackgroundTask(); - } - else - { - SaveTask->StartSynchronousTask(); - - if (!bCaptureThumbnail) - { - Finish(true); - } + SaveFileTask.Wait(); + Finish(SaveFileTask.GetResult()); } } diff --git a/Source/SaveExtension/Public/Misc/SlotHelpers.h b/Source/SaveExtension/Public/Misc/SlotHelpers.h index 8acee7b..b2fb6dd 100644 --- a/Source/SaveExtension/Public/Misc/SlotHelpers.h +++ b/Source/SaveExtension/Public/Misc/SlotHelpers.h @@ -2,7 +2,7 @@ #pragma once -#include "SaveFileHelpers.h" +#include "SEFileHelpers.h" #include #include diff --git a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h b/Source/SaveExtension/Public/Multithreading/LoadFileTask.h deleted file mode 100644 index cea08f7..0000000 --- a/Source/SaveExtension/Public/Multithreading/LoadFileTask.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "SaveFileHelpers.h" -#include "SaveManager.h" -#include - - -///////////////////////////////////////////////////// -// FLoadFileTask -// Async task to load a File -class FLoadFileTask : public FNonAbandonableTask -{ -protected: - TWeakObjectPtr Manager; - const FString SlotName; - - TWeakObjectPtr LastSlot; - TWeakObjectPtr LastSlotData; - - TWeakObjectPtr Slot; - - -public: - explicit FLoadFileTask(USaveManager* Manager, USaveSlot* LastSlot, FStringView SlotName); - ~FLoadFileTask(); - - void DoWork(); - - /** Game thread */ - USaveSlot* GetInfo() const - { - return Slot.Get(); - } - - USaveSlotData* GetData() const - { - return Slot.IsValid()? Slot->GetData() : nullptr; - } - - TStatId GetStatId() const - { - RETURN_QUICK_DECLARE_CYCLE_STAT(FLoadFileTask, STATGROUP_ThreadPoolAsyncTasks); - } -}; diff --git a/Source/SaveExtension/Public/Multithreading/SaveFileTask.h b/Source/SaveExtension/Public/Multithreading/SaveFileTask.h deleted file mode 100644 index 8887cae..0000000 --- a/Source/SaveExtension/Public/Multithreading/SaveFileTask.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "SaveFileHelpers.h" - -#include - - -///////////////////////////////////////////////////// -// FSaveFileTask -// Async task to save a File -class FSaveFileTask : public FNonAbandonableTask -{ -protected: - USaveSlot* Info; - const FString SlotName; - const bool bUseCompression; - -public: - FSaveFileTask(USaveSlot* Info, const FString& InSlotName, const bool bInUseCompression) - : Info(Info) - , SlotName(InSlotName) - , bUseCompression(bInUseCompression) - {} - - void DoWork() - { - FSaveFileHelpers::SaveFile(SlotName, Info, bUseCompression); - } - - FORCEINLINE TStatId GetStatId() const - { - RETURN_QUICK_DECLARE_CYCLE_STAT(FSaveFileTask, STATGROUP_ThreadPoolAsyncTasks); - } -}; diff --git a/Source/SaveExtension/Public/SaveFileHelpers.h b/Source/SaveExtension/Public/SEFileHelpers.h similarity index 78% rename from Source/SaveExtension/Public/SaveFileHelpers.h rename to Source/SaveExtension/Public/SEFileHelpers.h index 22c29c2..bc3678a 100644 --- a/Source/SaveExtension/Public/SaveFileHelpers.h +++ b/Source/SaveExtension/Public/SEFileHelpers.h @@ -12,6 +12,7 @@ #include #include #include +#include class USaveSlot; @@ -88,8 +89,6 @@ struct FSaveFile bool bIsDataCompressed = false; TArray DataBytes; - //TArray ThumbnailBytes; - FSaveFile(); @@ -105,21 +104,24 @@ struct FSaveFile /** Based on GameplayStatics to add multi-threading */ -class SAVEEXTENSION_API FSaveFileHelpers +class SAVEEXTENSION_API FSEFileHelpers { public: - static bool SaveFile(FStringView SlotName, USaveSlot* Slot, const bool bUseCompression); + static bool SaveFileSync(USaveSlot* Slot, FStringView OverrideSlotName = {}, const bool bUseCompression = true); + static UE::Tasks::TTask SaveFile(USaveSlot* Slot, FString OverrideSlotName = {}, const bool bUseCompression = true); - // Not safe for Multi-threading - static bool LoadFile(FStringView SlotName, USaveSlot*& Slot, bool bLoadData, const UObject* Outer); + static USaveSlot* LoadFileSync(FStringView SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager); + static UE::Tasks::TTask LoadFile(FString SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager); static bool DeleteFile(FStringView SlotName); static bool FileExists(FStringView SlotName); static const FString& GetSaveFolder(); static FString GetSlotPath(FStringView SlotName); - static FString GetThumbnailPath(FStringView SlotName); static UObject* DeserializeObject( UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes); + + // @return the pipe used for save file operations + static class UE::Tasks::FPipe& GetPipe(); }; diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 76288cb..9a631ac 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -250,7 +250,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi { if (Slot) { - DeleteSlotByName(Slot->FileName); + DeleteSlotByName(Slot->Name); } } @@ -387,7 +387,7 @@ inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded { return false; } - return SaveSlot(Slot->FileName, bOverrideIfNeeded, bScreenshot, Size, OnSaved); + return SaveSlot(Slot->Name, bOverrideIfNeeded, bScreenshot, Size, OnSaved); } inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, @@ -399,7 +399,7 @@ inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, Result = ESEContinueOrFail::Failed; return; } - BPSaveSlotByName(Slot->FileName, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); + BPSaveSlotByName(Slot->Name, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); } inline bool USaveManager::SaveActiveSlot(bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) @@ -413,7 +413,7 @@ inline bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded { return false; } - return LoadSlot(Slot->FileName, OnLoaded); + return LoadSlot(Slot->Name, OnLoaded); } inline void USaveManager::BPLoadSlot( @@ -424,7 +424,7 @@ inline void USaveManager::BPLoadSlot( Result = ESEContinueOrFail::Failed; return; } - BPLoadSlotByName(Slot->FileName, Result, MoveTemp(LatentInfo)); + BPLoadSlotByName(Slot->Name, Result, MoveTemp(LatentInfo)); } inline void USaveManager::IterateSubscribedInterfaces(TFunction&& Callback) diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 3c98b6e..c862cf0 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -151,8 +151,8 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame public: /** Slot where this SaveInfo and its saveData are saved */ - UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) - FName FileName = TEXT("Default"); + UPROPERTY(BlueprintReadWrite, Category = SaveSlot) + FName Name = TEXT("Default"); UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) FText DisplayName; @@ -213,8 +213,8 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame bool IsFrameSplitLoad() const; bool IsFrameSplitSave() const; - bool IsMTFilesLoad() const; - bool IsMTFilesSave() const; + bool ShouldLoadFileAsync() const; + bool ShouldSaveFileAsync() const; UFUNCTION(BlueprintPure, Category = SaveSlot) bool IsLoadingOrSaving() const; diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index d7c7c6f..1864055 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -4,7 +4,6 @@ #include "Delegates.h" #include "ISaveExtension.h" -#include "Multithreading/LoadFileTask.h" #include "SaveSlot.h" #include "SaveSlotData.h" #include "SEDataTask.h" @@ -48,9 +47,7 @@ struct FSEDataTask_Load : public FSEDataTask int32 CurrentActorIndex = 0; TArray> CurrentLevelActors; - /** Start AsyncTasks */ - FAsyncTask* LoadDataTask; - /** End AsyncTasks */ + UE::Tasks::TTask LoadFileTask; ELoadDataTaskState LoadState = ELoadDataTaskState::NotStarted; @@ -87,16 +84,8 @@ struct FSEDataTask_Load : public FSEDataTask void RespawnActors(const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord); protected: - //~ Begin Files - void StartLoadingData(); - - USaveSlotData* GetLoadedData() const; - const bool IsDataLoaded() const - { - return LoadDataTask && LoadDataTask->IsDone(); - }; - //~ End Files - + void StartLoadingFile(); + bool CheckFileLoaded(); /** BEGIN Deserialization */ void BeforeDeserialize(); diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index b9f60cf..454e9a3 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -4,7 +4,6 @@ #include "Delegates.h" #include "ISaveExtension.h" -#include "Multithreading/SaveFileTask.h" #include "SaveSlotData.h" #include "SEDataTask.h" @@ -39,9 +38,7 @@ struct FSEDataTask_Save : public FSEDataTask TArray> CurrentLevelActors; /** End Async variables */ - /** Begin AsyncTasks */ - FAsyncTask* SaveTask = nullptr; - /** End AsyncTasks */ + UE::Tasks::TTask SaveFileTask; bool bWaitingThumbnail = false; diff --git a/Source/Test/Private/Files.spec.cpp b/Source/Test/Private/Files.spec.cpp index 6fc1f37..dcb3f54 100644 --- a/Source/Test/Private/Files.spec.cpp +++ b/Source/Test/Private/Files.spec.cpp @@ -3,7 +3,7 @@ #include "Automatron.h" #include "Helpers/TestActor.h" -#include +#include #include @@ -42,7 +42,7 @@ void FSaveSpec_Files::Define() TestTrue("Saved", SaveManager->SaveSlot(0)); - TestTrue("Info File exists in disk", FSaveFileHelpers::FileExists(TEXT("0"))); + TestTrue("Info File exists in disk", FSEFileHelpers::FileExists(TEXT("0"))); }); It("Can save files asynchronously", [this]() { @@ -52,13 +52,13 @@ void FSaveSpec_Files::Define() 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", FSaveFileHelpers::FileExists(TEXT("0"))); + TestTrue("Info File exists in disk", FSEFileHelpers::FileExists(TEXT("0"))); bFinishTick = true; })); TestTrue("Started Saving", bSaving); // Files shouldn't exist yet - TestFalse("Info File exists in disk", FSaveFileHelpers::FileExists(TEXT("0"))); + TestFalse("Info File exists in disk", FSEFileHelpers::FileExists(TEXT("0"))); TickWorldUntil(GetMainWorld(), true, [this](float) { return !bFinishTick; @@ -70,9 +70,8 @@ void FSaveSpec_Files::Define() TestTrue("Saved", SaveManager->SaveSlot(0)); - USaveSlot* Slot = nullptr; - TestTrue("File was loaded", FSaveFileHelpers::LoadFile(TEXT("0"), Slot, true, SaveManager)); - TestNotNull("Info is valid", Slot); + USaveSlot* Slot = FSEFileHelpers::LoadFileSync(TEXT("0"), nullptr, true, SaveManager); + TestNotNull("Slot is valid", Slot); TestNotNull("Data is valid", Slot->GetData()); }); From 3d544413aece7af6d11742cb5bab11c3b338add9 Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 4 Oct 2023 18:22:09 +0200 Subject: [PATCH 14/39] Save Subsystems TODO: Load subsystems --- .../Private/Serialization/SEDataTask_Save.cpp | 28 +++++++++++++++++++ Source/SaveExtension/Public/SaveSlot.h | 3 ++ Source/SaveExtension/Public/SaveSlotData.h | 6 ++-- .../Public/Serialization/SEDataTask_Save.h | 6 +--- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 3b0a192..0d46e6f 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -159,6 +159,7 @@ void FSEDataTask_Save::OnStart() Slot = Manager->GetActiveSlot(); SlotData = Slot->GetData(); + check(SlotData->GetClass() == Slot->DataClass); SlotData->CleanRecords(true); @@ -256,6 +257,9 @@ void FSEDataTask_Save::SerializeWorld() SELog(Slot, "World '" + World->GetName() + "'", FColor::Green, false, 1); + SubsystemFilter = Slot->SubsystemFilter; + SubsystemFilter.BakeAllowedClasses(); + const TArray& Levels = World->GetStreamingLevels(); PrepareAllLevels(Levels); @@ -269,6 +273,30 @@ void FSEDataTask_Save::SerializeWorld() FSEArchive Archive(MemoryWriter, false); GameInstance->Serialize(Archive); SlotData->GameInstance = MoveTemp(Record); + + SlotData->GameInstanceSubsystems.Reset(); + for(UGameInstanceSubsystem* Subsystem : GameInstance->GetSubsystemArray()) + { + if (SubsystemFilter.IsAllowed(Subsystem->GetClass())) + { + auto& SubsystemRecord = SlotData->GameInstanceSubsystems.Add_GetRef({Subsystem}); + FMemoryWriter SubsystemMemoryWriter(SubsystemRecord.Data, true); + FSEArchive Ar(SubsystemMemoryWriter, false); + Subsystem->Serialize(Ar); + } + } + } + + SlotData->WorldSubsystems.Reset(); + for(UWorldSubsystem* Subsystem : World->GetSubsystemArray()) + { + if (SubsystemFilter.IsAllowed(Subsystem->GetClass())) + { + auto& SubsystemRecord = SlotData->WorldSubsystems.Add_GetRef({Subsystem}); + FMemoryWriter SubsystemMemoryWriter(SubsystemRecord.Data, true); + FSEArchive Ar(SubsystemMemoryWriter, false); + Subsystem->Serialize(Ar); + } } SerializeLevel(World->GetCurrentLevel()); diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index c862cf0..b061ba1 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -102,6 +102,9 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") FSEComponentClassFilter ComponentFilter; + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") + FSEClassFilter SubsystemFilter{ USubsystem::StaticClass() }; + /** If true, will Save and Load levels when they are shown or hidden. * This includes level streaming and world composition. */ diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index 404deec..b7b12fc 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -36,11 +36,13 @@ class SAVEEXTENSION_API USaveSlotData : public USaveGame * Serialized manually for performance */ FObjectRecord GameInstance; + TArray GameInstanceSubsystems; + + TArray WorldSubsystems; + FPersistentLevelRecord RootLevel; TArray SubLevels; - TArray GameInstanceSubsystems; - TArray WorldSubsystems; void CleanRecords(bool bKeepSublevels); diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 454e9a3..5496d0d 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -31,12 +31,8 @@ struct FSEDataTask_Save : public FSEDataTask protected: TObjectPtr Slot; + FSEClassFilter SubsystemFilter; - /** Start Async variables */ - TWeakObjectPtr CurrentLevel; - TWeakObjectPtr CurrentSLevel; - TArray> CurrentLevelActors; - /** End Async variables */ UE::Tasks::TTask SaveFileTask; From dbeb3d4070f48a910cc487bd891be5f596424df2 Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 4 Oct 2023 19:21:05 +0200 Subject: [PATCH 15/39] Ignore SKEL classes in editor for filters --- .../Private/Misc/ClassFilter.cpp | 10 ++++++++ Source/SaveExtension/SaveExtension.Build.cs | 24 ++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Source/SaveExtension/Private/Misc/ClassFilter.cpp b/Source/SaveExtension/Private/Misc/ClassFilter.cpp index 618a64a..b400061 100644 --- a/Source/SaveExtension/Private/Misc/ClassFilter.cpp +++ b/Source/SaveExtension/Private/Misc/ClassFilter.cpp @@ -6,6 +6,10 @@ #include +#if WITH_EDITORONLY_DATA +#include +#endif + FSEClassFilter::FSEClassFilter(UClass* BaseClass) : BaseClass{BaseClass}, IgnoredClasses{} {} void FSEClassFilter::Merge(const FSEClassFilter& Other) @@ -49,6 +53,12 @@ void FSEClassFilter::BakeAllowedClasses() const BakedAllowedClasses.Add(AllowedClassPtr); GetDerivedClasses(AllowedClassPtr, ChildrenOfAllowedClasses); } + +#if WITH_EDITORONLY_DATA + ChildrenOfAllowedClasses.RemoveAllSwap([](UClass* Class){ + return FKismetEditorUtilities::IsClassABlueprintSkeleton(Class); + }, false); +#endif } { TRACE_CPUPROFILER_EVENT_SCOPE(Second Pass : Bake Classes); diff --git a/Source/SaveExtension/SaveExtension.Build.cs b/Source/SaveExtension/SaveExtension.Build.cs index 891befc..a320b5c 100644 --- a/Source/SaveExtension/SaveExtension.Build.cs +++ b/Source/SaveExtension/SaveExtension.Build.cs @@ -15,15 +15,23 @@ public SaveExtension(ReadOnlyTargetRules Target) : base(Target) PublicDependencyModuleNames.AddRange(new string[] { - "Core", - "Engine", - "Foliage", - "AIModule", - "CoreUObject", - "DeveloperSettings", - "ImageWrapper", - "NavigationSystem" + "Core", + "Engine", + "Foliage", + "AIModule", + "CoreUObject", + "DeveloperSettings", + "ImageWrapper", + "NavigationSystem" }); + + if (Target.Type == TargetType.Editor) + { + PrivateDependencyModuleNames.AddRange(new string[] + { + "UnrealEd" + }); + } } } From 74c74854e98f348017e8d1d6bad8388f01f828d0 Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 4 Oct 2023 19:40:44 +0200 Subject: [PATCH 16/39] Load subsystems --- .../Private/Serialization/SEDataTask_Load.cpp | 61 ++++++++++++------- .../Public/Serialization/SEDataTask_Load.h | 6 +- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index ace68de..38cab47 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -190,14 +190,51 @@ bool FSEDataTask_Load::CheckFileLoaded() void FSEDataTask_Load::BeforeDeserialize() { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::BeforeDeserialize); + + SubsystemFilter = Slot->SubsystemFilter; + SubsystemFilter.BakeAllowedClasses(); + UWorld* World = GetWorld(); // Set current game time to the saved value World->TimeSeconds = SlotData->TimeSeconds; - if (Slot->bStoreGameInstance) + auto* GameInstance = GetWorld()->GetGameInstance(); + if (IsValid(GameInstance) && Slot->bStoreGameInstance) + { + if (GameInstance->GetClass() == SlotData->GameInstance.Class) + { + // Serialize from Record Data + FMemoryReader MemoryReader(SlotData->GameInstance.Data, true); + FSEArchive Archive(MemoryReader, false); + GameInstance->Serialize(Archive); + } + + for(const FSubsystemRecord& SubsystemRecord : SlotData->GameInstanceSubsystems) + { + if (SubsystemRecord.IsValid() && SubsystemFilter.IsAllowed(SubsystemRecord.Class)) + { + if (USubsystem* Subsystem = GameInstance->GetSubsystemBase(SubsystemRecord.Class)) + { + FMemoryReader SubsystemMemoryReader(SubsystemRecord.Data, true); + FSEArchive Ar(SubsystemMemoryReader, false); + Subsystem->Serialize(Ar); + } + } + } + } + + for(const FSubsystemRecord& SubsystemRecord : SlotData->WorldSubsystems) { - DeserializeGameInstance(); + if (SubsystemRecord.IsValid() && SubsystemFilter.IsAllowed(SubsystemRecord.Class)) + { + if (USubsystem* Subsystem = World->GetSubsystemBase(SubsystemRecord.Class)) + { + FMemoryReader SubsystemMemoryReader(SubsystemRecord.Data, true); + FSEArchive Ar(SubsystemMemoryReader, false); + Subsystem->Serialize(Ar); + } + } } } @@ -450,26 +487,6 @@ void FSEDataTask_Load::RespawnActors(const TArray& Records, const } } -void FSEDataTask_Load::DeserializeGameInstance() -{ - bool bSuccess = true; - auto* GameInstance = GetWorld()->GetGameInstance(); - const FObjectRecord& Record = SlotData->GameInstance; - - if (!IsValid(GameInstance) || GameInstance->GetClass() != Record.Class) - bSuccess = false; - - if (bSuccess) - { - // Serialize from Record Data - FMemoryReader MemoryReader(Record.Data, true); - FSEArchive Archive(MemoryReader, false); - GameInstance->Serialize(Archive); - } - - SELog(Slot, "Game Instance '" + Record.Name.ToString() + "'", FColor::Green, !bSuccess, 1); -} - bool FSEDataTask_Load::DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord) { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeActor); diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index 1864055..90a8019 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -36,6 +36,7 @@ struct FSEDataTask_Load : public FSEDataTask FName SlotName; TObjectPtr Slot; + FSEClassFilter SubsystemFilter; FOnGameLoaded Delegate; @@ -104,11 +105,6 @@ struct FSEDataTask_Load : public FSEDataTask void FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const; - /** Deserializes Game Instance Object and its Properties. - * Requires 'SaveGameInstance' flag to be used. - */ - void DeserializeGameInstance(); - /** Serializes an actor into this Actor Record */ bool DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord); From 4d51a7cce7f2f31084f8bc352555f84caa1e01aa Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 4 Oct 2023 20:08:25 +0200 Subject: [PATCH 17/39] Removed unneccesary code --- .../ClassFilter/ClassFilterHelpers.h | 2 +- .../ClassFilter/ClassFilterNode.cpp | 2 +- .../Customizations/ClassFilter/SClassFilter.h | 2 +- .../SEClassFilterGraphPanelPinFactory.h | 2 +- .../Private/{Misc => }/ClassFilter.cpp | 2 +- .../Private/Misc/SlotHelpers.cpp | 34 ----------------- .../SaveExtension/Private/SEFileHelpers.cpp | 37 +++++++++++++++++++ Source/SaveExtension/Private/SaveManager.cpp | 4 +- .../Private/Serialization/SEDataTask.cpp | 11 ++++++ .../Private/Serialization/SEDataTask_Load.cpp | 5 +-- .../Private/Serialization/SEDataTask_Save.cpp | 3 +- .../Public/{Misc => }/ClassFilter.h | 0 Source/SaveExtension/Public/Delegates.h | 16 -------- Source/SaveExtension/Public/LevelFilter.h | 2 +- .../SaveExtension/Public/Misc/SlotHelpers.h | 36 ------------------ Source/SaveExtension/Public/Misc/TypeTraits.h | 34 ----------------- Source/SaveExtension/Public/SEFileHelpers.h | 3 ++ Source/SaveExtension/Public/SaveManager.h | 11 +++++- .../Public/Serialization/SEDataTask.h | 4 ++ .../Public/Serialization/SEDataTask_Load.h | 2 +- .../Public/Serialization/SEDataTask_Save.h | 4 +- 21 files changed, 79 insertions(+), 137 deletions(-) rename Source/SaveExtension/Private/{Misc => }/ClassFilter.cpp (99%) delete mode 100644 Source/SaveExtension/Private/Misc/SlotHelpers.cpp rename Source/SaveExtension/Public/{Misc => }/ClassFilter.h (100%) delete mode 100644 Source/SaveExtension/Public/Delegates.h delete mode 100644 Source/SaveExtension/Public/Misc/SlotHelpers.h delete mode 100644 Source/SaveExtension/Public/Misc/TypeTraits.h diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h index 41ac0ca..63b26d3 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h @@ -2,7 +2,7 @@ #pragma once #include "ClassFilterNode.h" -#include "Misc/ClassFilter.h" +#include "ClassFilter.h" #include #include diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp index 9d72998..3b9275b 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterNode.cpp @@ -2,7 +2,7 @@ #include "ClassFilterNode.h" -#include "Misc/ClassFilter.h" +#include "ClassFilter.h" #include #include diff --git a/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h b/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h index bb566cb..4235d97 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h +++ b/Source/Editor/Private/Customizations/ClassFilter/SClassFilter.h @@ -5,7 +5,7 @@ #include "CoreMinimal.h" #include "Input/Reply.h" #include "Layout/Visibility.h" -#include "Misc/ClassFilter.h" +#include "ClassFilter.h" #include "SlateFwd.h" #include "UObject/Object.h" #include "Widgets/DeclarativeSyntaxSupport.h" diff --git a/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h b/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h index e76d367..447c6fd 100644 --- a/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h +++ b/Source/Editor/Private/Customizations/SEClassFilterGraphPanelPinFactory.h @@ -5,7 +5,7 @@ #include "EdGraphSchema_K2.h" #include "EdGraphUtilities.h" #include "GameplayTagContainer.h" -#include "Misc/ClassFilter.h" +#include "ClassFilter.h" #include "SClassFilterGraphPin.h" #include "SGraphPin.h" #include "Widgets/DeclarativeSyntaxSupport.h" diff --git a/Source/SaveExtension/Private/Misc/ClassFilter.cpp b/Source/SaveExtension/Private/ClassFilter.cpp similarity index 99% rename from Source/SaveExtension/Private/Misc/ClassFilter.cpp rename to Source/SaveExtension/Private/ClassFilter.cpp index b400061..16b3a52 100644 --- a/Source/SaveExtension/Private/Misc/ClassFilter.cpp +++ b/Source/SaveExtension/Private/ClassFilter.cpp @@ -1,6 +1,6 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. -#include "Misc/ClassFilter.h" +#include "ClassFilter.h" #include #include diff --git a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp b/Source/SaveExtension/Private/Misc/SlotHelpers.cpp deleted file mode 100644 index 500c804..0000000 --- a/Source/SaveExtension/Private/Misc/SlotHelpers.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#include "Misc/SlotHelpers.h" - -#include -#include - - -void FSlotHelpers::FindSlotFileNames(TArray& FoundSlots) -{ - FFindSlotVisitor Visitor{FoundSlots}; - FPlatformFileManager::Get().GetPlatformFile().IterateDirectory( - *FSEFileHelpers::GetSaveFolder(), Visitor); -} - -bool FSlotHelpers::FFindSlotVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) -{ - if (bIsDirectory) - { - return true; - } - - const FString FullFilePath(FilenameOrDirectory); - - FString Folder; - FString Filename; - FString Extension; - FPaths::Split(FullFilePath, Folder, Filename, Extension); - if (Extension == TEXT("sav")) - { - FoundSlots.Add(Filename); - } - return true; -} diff --git a/Source/SaveExtension/Private/SEFileHelpers.cpp b/Source/SaveExtension/Private/SEFileHelpers.cpp index a49c339..e534aa5 100644 --- a/Source/SaveExtension/Private/SEFileHelpers.cpp +++ b/Source/SaveExtension/Private/SEFileHelpers.cpp @@ -14,6 +14,7 @@ #include #include #include +#include static const int SE_SAVEGAME_FILE_TYPE_TAG = 0x0001; // "sAvG" @@ -21,6 +22,35 @@ static const int SE_SAVEGAME_FILE_TYPE_TAG = 0x0001; // "sAvG" UE::Tasks::FPipe BackendPipe{ TEXT("SaveExtensionPipe") }; +/** Used to find next available slot id */ +class FSEFindSlotVisitor : public IPlatformFile::FDirectoryVisitor +{ +public: + TArray& FoundSlots; + + FSEFindSlotVisitor(TArray& FoundSlots) : FoundSlots(FoundSlots) {} + virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override + { + if (bIsDirectory) + { + return true; + } + + const FString FullFilePath(FilenameOrDirectory); + + FString Folder; + FString Filename; + FString Extension; + FPaths::Split(FullFilePath, Folder, Filename, Extension); + if (Extension == TEXT("sav")) + { + FoundSlots.Add(Filename); + } + return true; + } +}; + + struct FSaveGameFileVersion { enum Type @@ -313,6 +343,13 @@ FString FSEFileHelpers::GetSlotPath(FStringView SlotName) return GetSaveFolder() / FString::Printf(TEXT("%s.sav"), SlotName.GetData()); } +void FSEFileHelpers::FindAllFilesSync(TArray& FoundSlots) +{ + FSEFindSlotVisitor Visitor{FoundSlots}; + FPlatformFileManager::Get().GetPlatformFile().IterateDirectory( + *FSEFileHelpers::GetSaveFolder(), Visitor); +} + UObject* FSEFileHelpers::DeserializeObject(UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes) { UObject* Object = Hint; diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 4fb7d3e..1c763f5 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -284,7 +284,7 @@ void USaveManager::PreloadAllSlots(FSEOnAllSlotsPreloaded Callback, bool bSortBy void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRecent) { TArray FileNames; - FSlotHelpers::FindSlotFileNames(FileNames); + FSEFileHelpers::FindAllFilesSync(FileNames); TArray LoadedFiles; LoadedFiles.Reserve(FileNames.Num()); @@ -335,7 +335,7 @@ void USaveManager::DeleteSlotByName(FName SlotName) int32 USaveManager::DeleteAllSlotsSync() { TArray FoundSlots; - FSlotHelpers::FindSlotFileNames(FoundSlots); + FSEFileHelpers::FindAllFilesSync(FoundSlots); int32 Count = 0; for (const FString& SlotName : FoundSlots) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp index fe47f1a..43af450 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp @@ -50,3 +50,14 @@ UWorld* FSEDataTask::GetWorld() const { return Manager->GetWorld(); } + +FString FSEDataTask::GetWorldName(const UWorld* World) +{ + check(World); + const FString MapName = World->GetOutermost()->GetName(); + if (World->IsPlayInEditor()) + { + return UWorld::RemovePIEPrefix(MapName); + } + return MapName; +} \ No newline at end of file diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 38cab47..b889249 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -2,7 +2,6 @@ #include "Serialization/SEDataTask_Load.h" -#include "Misc/SlotHelpers.h" #include "SaveManager.h" #include "Serialization/SEArchive.h" @@ -44,7 +43,7 @@ void FSEDataTask_Load::OnStart() // Cross-Level loading // TODO: Handle empty Map as empty world - FName CurrentMapName{FSlotHelpers::GetWorldName(World)}; + FName CurrentMapName{GetWorldName(World)}; if (CurrentMapName != Slot->Map) { LoadState = ELoadDataTaskState::LoadingMap; @@ -123,7 +122,7 @@ void FSEDataTask_Load::OnMapLoaded() UE_LOG(LogSaveExtension, Warning, TEXT("Failed loading map from saved slot.")); Finish(false); } - const FName NewMapName{FSlotHelpers::GetWorldName(World)}; + const FName NewMapName{GetWorldName(World)}; if (NewMapName == Slot->Map) { if (CheckFileLoaded()) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 0d46e6f..76b3153 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -2,7 +2,6 @@ #include "Serialization/SEDataTask_Save.h" -#include "Misc/SlotHelpers.h" #include "SEFileHelpers.h" #include "SaveManager.h" #include "SaveSlot.h" @@ -202,7 +201,7 @@ void FSEDataTask_Save::OnStart() } // Save Level info - Slot->Map = FName{FSlotHelpers::GetWorldName(World)}; + Slot->Map = FName{GetWorldName(World)}; SerializeWorld(); diff --git a/Source/SaveExtension/Public/Misc/ClassFilter.h b/Source/SaveExtension/Public/ClassFilter.h similarity index 100% rename from Source/SaveExtension/Public/Misc/ClassFilter.h rename to Source/SaveExtension/Public/ClassFilter.h diff --git a/Source/SaveExtension/Public/Delegates.h b/Source/SaveExtension/Public/Delegates.h deleted file mode 100644 index fbb9b04..0000000 --- a/Source/SaveExtension/Public/Delegates.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "SaveSlot.h" - - -/** Called when game has been saved - * @param SaveSlot the saved slot. Null if save failed - */ -DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); - -/** Called when game has been loaded - * @param SaveSlot the loaded slot. Null if load failed - */ -DECLARE_DELEGATE_OneParam(FOnGameLoaded, USaveSlot*); diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 8748961..4b5e8cd 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -2,7 +2,7 @@ #pragma once -#include "Misc/ClassFilter.h" +#include "ClassFilter.h" #include "LevelFilter.generated.h" diff --git a/Source/SaveExtension/Public/Misc/SlotHelpers.h b/Source/SaveExtension/Public/Misc/SlotHelpers.h deleted file mode 100644 index b2fb6dd..0000000 --- a/Source/SaveExtension/Public/Misc/SlotHelpers.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "SEFileHelpers.h" - -#include -#include - - -struct FSlotHelpers -{ - static void FindSlotFileNames(TArray& FoundSlots); - - /** Used to find next available slot id */ - class FFindSlotVisitor : public IPlatformFile::FDirectoryVisitor - { - public: - TArray& FoundSlots; - - FFindSlotVisitor(TArray& FoundSlots) : FoundSlots(FoundSlots) {} - - virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override; - }; - - static FString GetWorldName(const UWorld* World) - { - check(World); - const FString MapName = World->GetOutermost()->GetName(); - if (World->IsPlayInEditor()) - { - return UWorld::RemovePIEPrefix(MapName); - } - return MapName; - } -}; diff --git a/Source/SaveExtension/Public/Misc/TypeTraits.h b/Source/SaveExtension/Public/Misc/TypeTraits.h deleted file mode 100644 index e466846..0000000 --- a/Source/SaveExtension/Public/Misc/TypeTraits.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "Templates/UnrealTypeTraits.h" - - -template -constexpr bool VariadicContainsType() -{ - return false; -}; - -template -constexpr bool VariadicContainsType() -{ - return TIsSame::Value || VariadicContainsType(); -}; - - -template -constexpr uint32 GetVariadicTypeIndex() -{ - return Index + 1; -}; - -template -constexpr uint32 GetVariadicTypeIndex() -{ - if (TIsSame::Value) - return Index; - else - return GetVariadicTypeIndex(); -}; diff --git a/Source/SaveExtension/Public/SEFileHelpers.h b/Source/SaveExtension/Public/SEFileHelpers.h index bc3678a..df004c6 100644 --- a/Source/SaveExtension/Public/SEFileHelpers.h +++ b/Source/SaveExtension/Public/SEFileHelpers.h @@ -122,6 +122,9 @@ class SAVEEXTENSION_API FSEFileHelpers static UObject* DeserializeObject( UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes); + + static void FindAllFilesSync(TArray& FoundSlots); + // @return the pipe used for save file operations static class UE::Tasks::FPipe& GetPipe(); }; diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 9a631ac..7ae4c4b 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -2,7 +2,6 @@ #pragma once -#include "Delegates.h" #include "LevelStreamingNotifier.h" #include "SaveExtensionInterface.h" #include "SaveSlot.h" @@ -27,6 +26,16 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, Slot); using FSEOnAllSlotsPreloaded = TFunction& Slots)>; using FSEOnAllSlotsDeleted = TFunction; +/** Called when game has been saved + * @param SaveSlot the saved slot. Null if save failed + */ +DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); + +/** Called when game has been loaded + * @param SaveSlot the loaded slot. Null if load failed + */ +DECLARE_DELEGATE_OneParam(FOnGameLoaded, USaveSlot*); + UENUM() enum class ESEContinue : uint8 diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask.h b/Source/SaveExtension/Public/Serialization/SEDataTask.h index 23d70ee..90ec910 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask.h @@ -88,6 +88,10 @@ struct FSEDataTask } UWorld* GetWorld() const; + + +public: + static FString GetWorldName(const UWorld* World); }; diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index 90a8019..7c7bc11 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -2,10 +2,10 @@ #pragma once -#include "Delegates.h" #include "ISaveExtension.h" #include "SaveSlot.h" #include "SaveSlotData.h" +#include "SaveManager.h" #include "SEDataTask.h" #include diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 5496d0d..4e0942f 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -2,9 +2,9 @@ #pragma once -#include "Delegates.h" #include "ISaveExtension.h" #include "SaveSlotData.h" +#include "SaveManager.h" #include "SEDataTask.h" #include @@ -64,7 +64,7 @@ struct FSEDataTask_Save : public FSEDataTask // Where all magic happens virtual void OnStart() override; - virtual void Tick(float DeltaTime) override; + virtual void Tick(float DelLoadtaTime) override; virtual void OnFinish(bool bSuccess) override; protected: From 3e4118b884b7b336155947d5ac434d143aedd0c1 Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 5 Oct 2023 10:52:27 +0200 Subject: [PATCH 18/39] Cleaned includes --- .../Private/LifetimeComponent.cpp | 3 +- .../SaveExtension/Private/SEFileHelpers.cpp | 2 + .../SaveExtension/Private/SaveExtension.cpp | 27 +++++++++++ Source/SaveExtension/Private/SaveExtension.h | 25 ----------- Source/SaveExtension/Private/SaveManager.cpp | 4 +- Source/SaveExtension/Private/SaveSlot.cpp | 6 +-- .../Private/Serialization/SEDataTask.cpp | 7 +-- .../Private/Serialization/SEDataTask_Load.cpp | 45 ++++++++++++------- .../Serialization/SEDataTask_LoadLevel.cpp | 6 ++- .../Private/Serialization/SEDataTask_Save.cpp | 13 +++++- .../Serialization/SEDataTask_SaveLevel.cpp | 9 +++- Source/SaveExtension/Public/ClassFilter.h | 2 - Source/SaveExtension/Public/SEFileHelpers.h | 6 +-- .../{ISaveExtension.h => SaveExtension.h} | 43 ++++++------------ Source/SaveExtension/Public/SaveManager.h | 39 +++++----------- Source/SaveExtension/Public/SaveSettings.h | 1 - Source/SaveExtension/Public/SaveSlot.h | 2 +- Source/SaveExtension/Public/SaveSlotData.h | 5 +-- .../Public/Serialization/SEDataTask.h | 11 ++--- .../Public/Serialization/SEDataTask_Load.h | 22 ++++++--- .../Serialization/SEDataTask_LoadLevel.h | 1 - .../Public/Serialization/SEDataTask_Save.h | 21 ++++++--- .../Serialization/SEDataTask_SaveLevel.h | 6 +-- 23 files changed, 155 insertions(+), 151 deletions(-) delete mode 100644 Source/SaveExtension/Private/SaveExtension.h rename Source/SaveExtension/Public/{ISaveExtension.h => SaveExtension.h} (53%) diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index 0044606..59fda04 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -1,8 +1,7 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. #include "LifetimeComponent.h" - - +#include "SaveExtension.h" ULifetimeComponent::ULifetimeComponent() : Super() {} diff --git a/Source/SaveExtension/Private/SEFileHelpers.cpp b/Source/SaveExtension/Private/SEFileHelpers.cpp index e534aa5..5e4dccb 100644 --- a/Source/SaveExtension/Private/SEFileHelpers.cpp +++ b/Source/SaveExtension/Private/SEFileHelpers.cpp @@ -2,9 +2,11 @@ #include "SEFileHelpers.h" +#include "SaveExtension.h" #include "Serialization/SEArchive.h" #include "SaveSlot.h" #include "SaveSlotData.h" +#include "SaveManager.h" #include #include diff --git a/Source/SaveExtension/Private/SaveExtension.cpp b/Source/SaveExtension/Private/SaveExtension.cpp index 7786a64..b3a5ede 100644 --- a/Source/SaveExtension/Private/SaveExtension.cpp +++ b/Source/SaveExtension/Private/SaveExtension.cpp @@ -6,3 +6,30 @@ DEFINE_LOG_CATEGORY(LogSaveExtension) IMPLEMENT_MODULE(FSaveExtension, SaveExtension); + +void FSaveExtension::Log(const USaveSlot* Slot, const FString& Message, FColor Color, bool bError, const float Duration) + { + if (Slot->bDebug) + { + if (bError) + { + Color = FColor::Red; + } + + const FString ComposedMessage{FString::Printf(TEXT("SE: %s"), *Message)}; + + if (bError) + { + UE_LOG(LogSaveExtension, Error, TEXT("%s"), *ComposedMessage); + } + else + { + UE_LOG(LogSaveExtension, Log, TEXT("%s"), *ComposedMessage); + } + + if (Slot->bDebugInScreen && GEngine) + { + GEngine->AddOnScreenDebugMessage(-1, Duration, Color, ComposedMessage); + } + } + } diff --git a/Source/SaveExtension/Private/SaveExtension.h b/Source/SaveExtension/Private/SaveExtension.h deleted file mode 100644 index 6b88367..0000000 --- a/Source/SaveExtension/Private/SaveExtension.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2024 Piperift. All Rights Reserved. - -#pragma once - -#include "ISaveExtension.h" - -#if WITH_EDITORONLY_DATA -# 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; - } -}; diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index 1c763f5..e8ec793 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -2,13 +2,15 @@ #include "SaveManager.h" -#include "SEFileHelpers.h" +#include "SaveExtension.h" #include "SaveSettings.h" +#include "SEFileHelpers.h" #include "Serialization/SEDataTask_LoadLevel.h" #include "Serialization/SEDataTask_SaveLevel.h" #include "Serialization/SEDataTask_Load.h" #include "Serialization/SEDataTask_Save.h" +#include #include #include #include diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 4c05142..b9a4995 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -6,10 +6,8 @@ #include #include -#include +#include #include -#include -#include #include #include @@ -147,7 +145,9 @@ void USaveSlot::OnThumbnailCaptured(int32 InSizeX, int32 InSizeY, const TArrayDeferCompression = true; +#endif FColor* TextureData = static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE)); for(int32 i = 0; i < InImageData.Num(); ++i, ++TextureData) { diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp index 43af450..0dd607e 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask.cpp @@ -3,6 +3,7 @@ #include "Serialization/SEDataTask.h" #include "SaveManager.h" +#include "SaveSlotData.h" ///////////////////////////////////////////////////// @@ -37,13 +38,13 @@ bool FSEDataTask::IsScheduled() const }); } -FLevelRecord* FSEDataTask::FindLevelRecord(const ULevelStreaming* Level) const +FLevelRecord* FSEDataTask::FindLevelRecord(USaveSlotData& Data, const ULevelStreaming* Level) const { if (Level) { - return SlotData->SubLevels.FindByKey(Level); + return Data.SubLevels.FindByKey(Level); } - return &SlotData->RootLevel; + return &Data.RootLevel; } UWorld* FSEDataTask::GetWorld() const diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index b889249..febdde4 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -2,7 +2,12 @@ #include "Serialization/SEDataTask_Load.h" +#include "SEFileHelpers.h" +#include "SaveExtension.h" #include "SaveManager.h" +#include "SaveSlot.h" +#include "SaveSlotData.h" +#include "Serialization/Records.h" #include "Serialization/SEArchive.h" #include @@ -15,6 +20,12 @@ ///////////////////////////////////////////////////// // USaveDataTask_Loader +FSEDataTask_Load::FSEDataTask_Load(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask(Manager, ESETaskType::Load) + , SlotData(Slot->GetData()) + , MaxFrameMs(Slot->GetMaxFrameMs()) +{} + FSEDataTask_Load::~FSEDataTask_Load() { if (!LoadFileTask.IsCompleted()) @@ -209,7 +220,7 @@ void FSEDataTask_Load::BeforeDeserialize() GameInstance->Serialize(Archive); } - for(const FSubsystemRecord& SubsystemRecord : SlotData->GameInstanceSubsystems) + for (const FSubsystemRecord& SubsystemRecord : SlotData->GameInstanceSubsystems) { if (SubsystemRecord.IsValid() && SubsystemFilter.IsAllowed(SubsystemRecord.Class)) { @@ -223,7 +234,7 @@ void FSEDataTask_Load::BeforeDeserialize() } } - for(const FSubsystemRecord& SubsystemRecord : SlotData->WorldSubsystems) + for (const FSubsystemRecord& SubsystemRecord : SlotData->WorldSubsystems) { if (SubsystemRecord.IsValid() && SubsystemFilter.IsAllowed(SubsystemRecord.Class)) { @@ -265,8 +276,7 @@ void FSEDataTask_Load::DeserializeSync() FinishedDeserializing(); } -void FSEDataTask_Load::DeserializeLevelSync( - const ULevel* Level, const ULevelStreaming* StreamingLevel) +void FSEDataTask_Load::DeserializeLevelSync(const ULevel* Level, const ULevelStreaming* StreamingLevel) { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::DeserializeLevelSync); @@ -279,8 +289,8 @@ void FSEDataTask_Load::DeserializeLevelSync( StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); - const FLevelRecord& LevelRecord = *FindLevelRecord(StreamingLevel); - for(const auto& RecordToActor : LevelRecord.RecordsToActors) + const FLevelRecord& LevelRecord = *FindLevelRecord(*SlotData, StreamingLevel); + for (const auto& RecordToActor : LevelRecord.RecordsToActors) { const FActorRecord* Record = RecordToActor.Key; AActor* Actor = RecordToActor.Value.Get(); @@ -308,7 +318,7 @@ void FSEDataTask_Load::DeserializeLevelASync(ULevel* Level, ULevelStreaming* Str StreamingLevel ? StreamingLevel->GetWorldAssetPackageFName() : FPersistentLevelRecord::PersistentName; SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); - FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); + FLevelRecord* LevelRecord = FindLevelRecord(*SlotData, StreamingLevel); if (!LevelRecord) { Finish(false); @@ -331,7 +341,7 @@ void FSEDataTask_Load::DeserializeASyncLoop(float StartMS) StartMS = GetTimeMilliseconds(); } - FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); + FLevelRecord& LevelRecord = *FindLevelRecord(*SlotData, CurrentSLevel.Get()); // Continue Iterating actors every tick for (; CurrentActorIndex < LevelRecord.RecordsToActors.Num(); ++CurrentActorIndex) @@ -391,7 +401,7 @@ void FSEDataTask_Load::PrepareLevel(const ULevel* Level, FLevelRecord& LevelReco } TArray ActorsToDestroy{}; - { // Filter actors by whether they should be destroyed or spawned - O(M*Log(N)) + { // Filter actors by whether they should be destroyed or spawned - O(M*Log(N)) for (AActor* const Actor : Level->Actors) { if (UNLIKELY(!Actor)) @@ -402,7 +412,7 @@ void FSEDataTask_Load::PrepareLevel(const ULevel* Level, FLevelRecord& LevelReco 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 (Index != INDEX_NONE) // Actor found, therefore doesn't need to be spawned { if (LevelRecord.Filter.Stores(Actor)) { @@ -417,7 +427,6 @@ void FSEDataTask_Load::PrepareLevel(const ULevel* Level, FLevelRecord& LevelReco } // TODO: Consider unmatching class actors to be respawned } - } // The serializable actors that were not found will be destroyed @@ -455,7 +464,7 @@ void FSEDataTask_Load::PrepareAllLevels() { if (Level->IsLevelLoaded()) { - FLevelRecord* LevelRecord = FindLevelRecord(Level); + FLevelRecord* LevelRecord = FindLevelRecord(*SlotData, Level); if (LevelRecord) { PrepareLevel(Level->GetLoadedLevel(), *LevelRecord); @@ -464,7 +473,8 @@ void FSEDataTask_Load::PrepareAllLevels() } } -void FSEDataTask_Load::RespawnActors(const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord) +void FSEDataTask_Load::RespawnActors( + const TArray& Records, const ULevel* Level, FLevelRecord& LevelRecord) { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Load::RespawnActors); @@ -486,13 +496,15 @@ void FSEDataTask_Load::RespawnActors(const TArray& Records, const } } -bool FSEDataTask_Load::DeserializeActor(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord) +bool FSEDataTask_Load::DeserializeActor( + AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord) { 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); + SELog(Slot, "Actor '" + ActorRecord.Name.ToString() + "' already exists but class doesn't match", + FColor::Green, true, 1); return false; } @@ -533,7 +545,8 @@ bool FSEDataTask_Load::DeserializeActor(AActor* Actor, const FActorRecord& Actor return true; } -void FSEDataTask_Load::DeserializeActorComponents(AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 Indent) +void FSEDataTask_Load::DeserializeActorComponents( + AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 Indent) { TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents); diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp index cfcd469..3697713 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp @@ -2,6 +2,8 @@ #include "Serialization/SEDataTask_LoadLevel.h" +#include "SaveSlot.h" + ///////////////////////////////////////////////////// // USaveDataTask_LevelLoader @@ -14,7 +16,7 @@ void FSEDataTask_LoadLevel::OnStart() return; } - FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); + FLevelRecord* LevelRecord = FindLevelRecord(*SlotData, StreamingLevel); if (!LevelRecord) { Finish(false); @@ -43,7 +45,7 @@ void FSEDataTask_LoadLevel::DeserializeASyncLoop(float StartMS /*= 0.0f*/) StartMS = GetTimeMilliseconds(); } - FLevelRecord& LevelRecord = *FindLevelRecord(CurrentSLevel.Get()); + FLevelRecord& LevelRecord = *FindLevelRecord(*SlotData, CurrentSLevel.Get()); // Continue Iterating actors every tick for (; CurrentActorIndex < LevelRecord.RecordsToActors.Num(); ++CurrentActorIndex) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 76b3153..7dba960 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -2,10 +2,14 @@ #include "Serialization/SEDataTask_Save.h" -#include "SEFileHelpers.h" +#include "SaveExtension.h" #include "SaveManager.h" #include "SaveSlot.h" #include "SaveSlotData.h" +#include "SEFileHelpers.h" +#include "Serialization/Records.h" +#include "Serialization/SEArchive.h" +#include "SEFileHelpers.h" #include #include @@ -112,6 +116,11 @@ bool SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFil ///////////////////////////////////////////////////// // FSEDataTask_Save +FSEDataTask_Save::FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) + : FSEDataTask(Manager, ESETaskType::Save) + , SlotData(Slot->GetData()) +{} + FSEDataTask_Save::~FSEDataTask_Save() { if (!SaveFileTask.IsCompleted()) @@ -342,7 +351,7 @@ void FSEDataTask_Save::SerializeLevel( SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level - auto& LevelRecord = StreamingLevel? *FindLevelRecord(StreamingLevel) : SlotData->RootLevel; + auto& LevelRecord = StreamingLevel? *FindLevelRecord(*SlotData, StreamingLevel) : SlotData->RootLevel; const FSELevelFilter& Filter = LevelRecord.Filter; LevelRecord.CleanRecords(); // Empty level record before serializing it diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp index 85e5adf..32c59e4 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_SaveLevel.cpp @@ -2,6 +2,8 @@ #include "Serialization/SEDataTask_SaveLevel.h" +#include "SaveExtension.h" + ///////////////////////////////////////////////////// // FSaveDataTask_LevelSaver @@ -10,7 +12,7 @@ void FSEDataTask_SaveLevel::OnStart() { if (SlotData && StreamingLevel && StreamingLevel->IsLevelLoaded()) { - FLevelRecord* LevelRecord = FindLevelRecord(StreamingLevel); + FLevelRecord* LevelRecord = FindLevelRecord(*SlotData, StreamingLevel); if (!LevelRecord) { Finish(false); @@ -26,3 +28,8 @@ void FSEDataTask_SaveLevel::OnStart() } Finish(false); } + +void FSEDataTask_SaveLevel::OnFinish(bool bSuccess) +{ + SELog(Slot, "Finished Serializing level", FColor::Green); +} diff --git a/Source/SaveExtension/Public/ClassFilter.h b/Source/SaveExtension/Public/ClassFilter.h index 7db8940..232017c 100644 --- a/Source/SaveExtension/Public/ClassFilter.h +++ b/Source/SaveExtension/Public/ClassFilter.h @@ -2,8 +2,6 @@ #pragma once -#include "CoreMinimal.h" - #include #include diff --git a/Source/SaveExtension/Public/SEFileHelpers.h b/Source/SaveExtension/Public/SEFileHelpers.h index df004c6..cb210d7 100644 --- a/Source/SaveExtension/Public/SEFileHelpers.h +++ b/Source/SaveExtension/Public/SEFileHelpers.h @@ -2,19 +2,15 @@ #pragma once -#include "ISaveExtension.h" - #include -#include -#include #include #include #include -#include #include #include +class USaveManager; class USaveSlot; class USaveSlotData; class FMemoryReader; diff --git a/Source/SaveExtension/Public/ISaveExtension.h b/Source/SaveExtension/Public/SaveExtension.h similarity index 53% rename from Source/SaveExtension/Public/ISaveExtension.h rename to Source/SaveExtension/Public/SaveExtension.h index f8b644c..9675ff1 100644 --- a/Source/SaveExtension/Public/ISaveExtension.h +++ b/Source/SaveExtension/Public/SaveExtension.h @@ -9,12 +9,20 @@ DECLARE_LOG_CATEGORY_EXTERN(LogSaveExtension, All, All); -class ISaveExtension : public IModuleInterface + +class FSaveExtension : public IModuleInterface { public: - static inline ISaveExtension& Get() + void StartupModule() override {} + void ShutdownModule() override {} + bool SupportsDynamicReloading() override { - return FModuleManager::LoadModuleChecked("SaveExtension"); + return true; + } + + static inline FSaveExtension& Get() + { + return FModuleManager::LoadModuleChecked("SaveExtension"); } static inline bool IsAvailable() { @@ -27,32 +35,7 @@ class ISaveExtension : public IModuleInterface } static void Log(const USaveSlot* Slot, const FString& Message, FColor Color = FColor::White, - bool bError = false, const float Duration = 2.f) - { - if (Slot->bDebug) - { - if (bError) - { - Color = FColor::Red; - } - - const FString ComposedMessage{FString::Printf(TEXT("SE: %s"), *Message)}; - - if (bError) - { - UE_LOG(LogSaveExtension, Error, TEXT("%s"), *ComposedMessage); - } - else - { - UE_LOG(LogSaveExtension, Log, TEXT("%s"), *ComposedMessage); - } - - if (Slot->bDebugInScreen && GEngine) - { - GEngine->AddOnScreenDebugMessage(-1, Duration, Color, ComposedMessage); - } - } - } + bool bError = false, const float Duration = 2.f); }; // Only log in Editor @@ -60,7 +43,7 @@ class ISaveExtension : public IModuleInterface template void SELog(Args&&... args) { - ISaveExtension::Log(Forward(args)...); + FSaveExtension::Log(Forward(args)...); } #else template diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 7ae4c4b..35a73a1 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -7,12 +7,9 @@ #include "SaveSlot.h" #include "SaveSlotData.h" #include "Serialization/SEDataTask.h" +#include "Serialization/SEDataTask_Load.h" +#include "Serialization/SEDataTask_Save.h" -#include -#include -#include -#include -#include #include #include @@ -20,22 +17,14 @@ struct FLatentActionInfo; +class UGameInstance; + DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameSavedMC, USaveSlot*, Slot); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGameLoadedMC, USaveSlot*, Slot); using FSEOnAllSlotsPreloaded = TFunction& Slots)>; using FSEOnAllSlotsDeleted = TFunction; -/** Called when game has been saved - * @param SaveSlot the saved slot. Null if save failed - */ -DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); - -/** Called when game has been loaded - * @param SaveSlot the loaded slot. Null if load failed - */ -DECLARE_DELEGATE_OneParam(FOnGameLoaded, USaveSlot*); - UENUM() enum class ESEContinue : uint8 @@ -134,8 +123,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Save the currently loaded Slot */ - bool SaveActiveSlot( - bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); + bool SaveActiveSlot(bool bScreenshot = false, const FScreenshotSize Size = {}, FOnGameSaved OnSaved = {}); /** Load game from a file name */ @@ -210,14 +198,14 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /** Load game from a slot name */ UFUNCTION(BlueprintCallable, Category = "SaveExtension|Loading", - meta = (DisplayName = "Load Slot by Name", Latent, LatentInfo = "LatentInfo", ExpandEnumAsExecs = "Result", - UnsafeDuringActorConstruction)) + meta = (DisplayName = "Load Slot by Name", Latent, LatentInfo = "LatentInfo", + ExpandEnumAsExecs = "Result", UnsafeDuringActorConstruction)) void BPLoadSlotByName(FName SlotName, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Load game from a Slot */ 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(const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo); /** Reload the currently loaded slot if any */ @@ -314,9 +302,7 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi template TaskType& CreateTask() { - return static_cast( - *Tasks.Add_GetRef(MakeUnique(this, ActiveSlot)) - ); + return static_cast(*Tasks.Add_GetRef(MakeUnique(this, ActiveSlot))); } void FinishTask(FSEDataTask* Task); @@ -399,9 +385,8 @@ inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded return SaveSlot(Slot->Name, bOverrideIfNeeded, bScreenshot, Size, OnSaved); } -inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, - const FScreenshotSize Size, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, - bool bOverrideIfNeeded) +inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, const FScreenshotSize Size, + ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) { if (!Slot) { diff --git a/Source/SaveExtension/Public/SaveSettings.h b/Source/SaveExtension/Public/SaveSettings.h index 214d652..00241ad 100644 --- a/Source/SaveExtension/Public/SaveSettings.h +++ b/Source/SaveExtension/Public/SaveSettings.h @@ -4,7 +4,6 @@ #include "SaveSlot.h" -#include #include #include "SaveSettings.generated.h" diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index b061ba1..0e7cf95 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -5,12 +5,12 @@ #include "SaveSlotData.h" #include -#include #include #include "SaveSlot.generated.h" struct FSELevelFilter; +class UTexture2D; DECLARE_DELEGATE_OneParam(FSEOnThumbnailCaptured, bool); diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index b7b12fc..253341d 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -10,7 +10,6 @@ #include #include #include -#include #include "SaveSlotData.generated.h" @@ -20,9 +19,9 @@ * Works like a common SaveGame object * E.g: Items, Quests, Enemies, World Actors, AI, Physics */ -UCLASS(ClassGroup = SaveExtension, hideCategories = ("Activation", "Actor Tick", "Actor", "Input", +UCLASS(Blueprintable, BlueprintType, ClassGroup = SaveExtension, hideCategories = ("Activation", "Actor Tick", "Actor", "Input", "Rendering", "Replication", "Socket", "Thumbnail")) -class SAVEEXTENSION_API USaveSlotData : public USaveGame +class SAVEEXTENSION_API USaveSlotData : public UObject { GENERATED_BODY() diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask.h b/Source/SaveExtension/Public/Serialization/SEDataTask.h index 90ec910..3357624 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask.h @@ -2,7 +2,6 @@ #pragma once -#include "ISaveExtension.h" #include "LevelFilter.h" #include "SaveSlotData.h" @@ -12,6 +11,8 @@ class USaveManager; +class USaveSlotData; + enum class ESETaskType : uint8 { @@ -34,16 +35,12 @@ struct FSEDataTask protected: TObjectPtr Manager; - TObjectPtr SlotData; - float MaxFrameMs = 0.f; public: - FSEDataTask(USaveManager* Manager, USaveSlot* Slot, ESETaskType Type) + FSEDataTask(USaveManager* Manager, ESETaskType Type) : Type(Type) , Manager(Manager) - , SlotData(Slot->GetData()) - , MaxFrameMs(Slot->GetMaxFrameMs()) {} virtual ~FSEDataTask() = default; @@ -80,7 +77,7 @@ struct FSEDataTask void BakeAllFilters(); - FLevelRecord* FindLevelRecord(const ULevelStreaming* Level) const; + FLevelRecord* FindLevelRecord(USaveSlotData& Data, const ULevelStreaming* Level) const; float GetTimeMilliseconds() const { diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index 7c7bc11..d67659b 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -2,10 +2,6 @@ #pragma once -#include "ISaveExtension.h" -#include "SaveSlot.h" -#include "SaveSlotData.h" -#include "SaveManager.h" #include "SEDataTask.h" #include @@ -13,6 +9,18 @@ #include #include #include +#include + + +class USaveManager; +class USaveSlot; +class USaveSlotData; + + +/** Called when game has been loaded + * @param SaveSlot the loaded slot. Null if load failed + */ +DECLARE_DELEGATE_OneParam(FOnGameLoaded, USaveSlot*); enum class ELoadDataTaskState : uint8 @@ -36,6 +44,8 @@ struct FSEDataTask_Load : public FSEDataTask FName SlotName; TObjectPtr Slot; + TObjectPtr SlotData; + float MaxFrameMs = 0.f; FSEClassFilter SubsystemFilter; FOnGameLoaded Delegate; @@ -54,9 +64,7 @@ struct FSEDataTask_Load : public FSEDataTask public: - FSEDataTask_Load(USaveManager* Manager, USaveSlot* Slot) - : FSEDataTask(Manager, Slot, ESETaskType::Load) - {} + FSEDataTask_Load(USaveManager* Manager, USaveSlot* Slot); ~FSEDataTask_Load(); auto& Setup(FName InSlotName) diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h b/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h index f2a5665..f575fc1 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_LoadLevel.h @@ -2,7 +2,6 @@ #pragma once -#include "ISaveExtension.h" #include "SEDataTask_Load.h" diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h index 4e0942f..6852ebc 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Save.h @@ -2,18 +2,26 @@ #pragma once -#include "ISaveExtension.h" -#include "SaveSlotData.h" -#include "SaveManager.h" #include "SEDataTask.h" #include -#include #include #include #include #include #include +#include + + +class USaveManager; +class USaveSlot; +class USaveSlotData; + + +/** Called when game has been saved + * @param SaveSlot the saved slot. Null if save failed + */ +DECLARE_DELEGATE_OneParam(FOnGameSaved, USaveSlot*); /** @@ -31,6 +39,7 @@ struct FSEDataTask_Save : public FSEDataTask protected: TObjectPtr Slot; + TObjectPtr SlotData; FSEClassFilter SubsystemFilter; @@ -39,9 +48,7 @@ struct FSEDataTask_Save : public FSEDataTask bool bWaitingThumbnail = false; public: - FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) - : FSEDataTask(Manager, Slot, ESETaskType::Save) - {} + FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot); ~FSEDataTask_Save(); auto& Setup( diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h b/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h index ede2300..e03d0c2 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_SaveLevel.h @@ -2,7 +2,6 @@ #pragma once -#include "ISaveExtension.h" #include "SEDataTask_Save.h" @@ -27,8 +26,5 @@ struct FSEDataTask_SaveLevel : public FSEDataTask_Save private: virtual void OnStart() override; - virtual void OnFinish(bool bSuccess) override - { - SELog(Slot, "Finished Serializing level", FColor::Green); - } + virtual void OnFinish(bool bSuccess) override; }; From 6c9ac88e980c7334bae3552cf99556fea3620021 Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 5 Oct 2023 17:41:13 +0200 Subject: [PATCH 19/39] Simplified filters, fixed slot details --- .../Customizations/SaveSlotDetails.cpp | 66 +++++++++---------- .../Private/Customizations/SaveSlotDetails.h | 2 - Source/Editor/Private/SaveExtensionEditor.cpp | 4 -- .../Private/Serialization/SEArchive.cpp | 2 +- Source/SaveExtension/Public/ClassFilter.h | 20 ------ Source/SaveExtension/Public/LevelFilter.h | 4 +- Source/SaveExtension/Public/SaveSlot.h | 55 ++++++++-------- 7 files changed, 62 insertions(+), 91 deletions(-) diff --git a/Source/Editor/Private/Customizations/SaveSlotDetails.cpp b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp index 7133195..857e616 100644 --- a/Source/Editor/Private/Customizations/SaveSlotDetails.cpp +++ b/Source/Editor/Private/Customizations/SaveSlotDetails.cpp @@ -28,37 +28,42 @@ void FSaveSlotDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) TArray> Objects; DetailBuilder.GetObjectsBeingCustomized(Objects); + TSharedRef MultithreadedSerialization = DetailBuilder.GetProperty(FName("MultithreadedSerialization"), USaveSlot::StaticClass()); + TSharedRef FrameSplittedSerialization = DetailBuilder.GetProperty(FName("FrameSplittedSerialization"), USaveSlot::StaticClass()); + TSharedRef MaxFrameMs = DetailBuilder.GetProperty(FName("MaxFrameMs"), USaveSlot::StaticClass()); + if (Objects.Num() && Objects[0] != nullptr) { Slot = CastChecked(Objects[0].Get()); - DetailBuilder.EditCategory(TEXT("Gameplay")); + // Sort categories + DetailBuilder.EditCategory(TEXT("Slot")); + DetailBuilder.EditCategory(TEXT("Automatic")); DetailBuilder.EditCategory(TEXT("Serialization")); - - IDetailCategoryBuilder& Category = DetailBuilder.EditCategory(TEXT("Asynchronous")); - { - Category.AddProperty(TEXT("MultithreadedSerialization")); - Category.AddProperty(TEXT("FrameSplittedSerialization")); - Category.AddCustomRow(LOCTEXT("AsyncWarning", "Asynchronous Warning")) - .Visibility({this, &FSaveSlotDetails::GetWarningVisibility}) - .ValueContent() - .MinDesiredWidth(300.f) - .MaxDesiredWidth( - 400.f)[SNew(SBorder) - .Padding(2.f) - .BorderImage(FAppStyle::GetBrush("ErrorReporting.EmptyBox")) - .BorderBackgroundColor(this, &FSaveSlotDetails::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, &FSaveSlotDetails::CanEditAsynchronous}, nullptr); - } - - DetailBuilder.EditCategory(TEXT("Level Streaming")); + DetailBuilder.EditCategory(TEXT("Files")); + DetailBuilder.EditCategory(TEXT("Async")); + + DetailBuilder.AddPropertyToCategory(MultithreadedSerialization); + DetailBuilder.AddPropertyToCategory(FrameSplittedSerialization); + DetailBuilder.AddCustomRowToCategory(FrameSplittedSerialization, LOCTEXT("AsyncWarning", "Asynchronous Warning")) + .Visibility({this, &FSaveSlotDetails::GetWarningVisibility}) + .ValueContent() + .MinDesiredWidth(300.f) + .MaxDesiredWidth(400.f) + [ + SNew(SBorder) + .Padding(2.f) + .BorderImage(FAppStyle::GetBrush("ErrorReporting.EmptyBox")) + .BorderBackgroundColor(this, &FSaveSlotDetails::GetWarningColor) + [ + SNew(STextBlock) + .Text(LOCTEXT("AsyncWarningText", + "WARNING: Frame-splitted loading or saving is not recommended " + "while using Level Streaming or World Composition")) + .AutoWrapText(true) + ] + ]; + DetailBuilder.AddPropertyToCategory(MaxFrameMs); } } @@ -77,13 +82,4 @@ EVisibility FSaveSlotDetails::GetWarningVisibility() const return EVisibility::Collapsed; } -bool FSaveSlotDetails::CanEditAsynchronous() const -{ - if (Slot.IsValid()) - { - return Slot->GetFrameSplitSerialization() != ESEAsyncMode::SaveAndLoadSync; - } - return true; -} - #undef LOCTEXT_NAMESPACE diff --git a/Source/Editor/Private/Customizations/SaveSlotDetails.h b/Source/Editor/Private/Customizations/SaveSlotDetails.h index 65ec884..4677b5e 100644 --- a/Source/Editor/Private/Customizations/SaveSlotDetails.h +++ b/Source/Editor/Private/Customizations/SaveSlotDetails.h @@ -29,8 +29,6 @@ class FSaveSlotDetails : public IDetailCustomization FSlateColor GetWarningColor() const; EVisibility GetWarningVisibility() const; - bool CanEditAsynchronous() const; - TWeakObjectPtr Slot; }; diff --git a/Source/Editor/Private/SaveExtensionEditor.cpp b/Source/Editor/Private/SaveExtensionEditor.cpp index 9f648e0..ff13acf 100644 --- a/Source/Editor/Private/SaveExtensionEditor.cpp +++ b/Source/Editor/Private/SaveExtensionEditor.cpp @@ -46,10 +46,6 @@ void FSaveExtensionEditor::RegisterPropertyTypeCustomizations() RegisterCustomPropertyTypeLayout("SEClassFilter", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout("SEActorClassFilter", - FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); - RegisterCustomPropertyTypeLayout("SEComponentClassFilter", - FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSEClassFilterCustomization::MakeInstance)); RegisterCustomPinFactory(); } diff --git a/Source/SaveExtension/Private/Serialization/SEArchive.cpp b/Source/SaveExtension/Private/Serialization/SEArchive.cpp index aea4249..71eb8dc 100644 --- a/Source/SaveExtension/Private/Serialization/SEArchive.cpp +++ b/Source/SaveExtension/Private/Serialization/SEArchive.cpp @@ -44,7 +44,7 @@ FArchive& FSEArchive::operator<<(UObject*& Obj) } else { - if (Obj && !Obj->IsTemplate() && !Obj->HasAnyFlags(RF_Transient)) + if (Obj) { // Serialize the fully qualified object name FString SavedString{Obj->GetPathName()}; diff --git a/Source/SaveExtension/Public/ClassFilter.h b/Source/SaveExtension/Public/ClassFilter.h index 232017c..1db6624 100644 --- a/Source/SaveExtension/Public/ClassFilter.h +++ b/Source/SaveExtension/Public/ClassFilter.h @@ -64,23 +64,3 @@ struct SAVEEXTENSION_API FSEClassFilter bool operator==(const FSEClassFilter& Other) const; }; - - -USTRUCT(BlueprintType) -struct FSEActorClassFilter : public FSEClassFilter -{ - GENERATED_BODY() - - FSEActorClassFilter() : Super(AActor::StaticClass()) {} - FSEActorClassFilter(TSubclassOf ActorClass) : Super(ActorClass) {} -}; - - -USTRUCT(BlueprintType) -struct FSEComponentClassFilter : public FSEClassFilter -{ - GENERATED_BODY() - - FSEComponentClassFilter() : Super(UActorComponent::StaticClass()) {} - FSEComponentClassFilter(TSubclassOf CompClass) : Super(CompClass) {} -}; diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 4b5e8cd..73d6413 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -27,10 +27,10 @@ struct FSELevelFilter public: UPROPERTY(SaveGame) - FSEActorClassFilter ActorFilter; + FSEClassFilter ActorFilter; UPROPERTY(SaveGame) - FSEComponentClassFilter ComponentFilter; + FSEClassFilter ComponentFilter; FSELevelFilter() = default; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 0e7cf95..729699d 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -65,60 +65,60 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame public: /** Begin Settings */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Slot") TSubclassOf DataClass = USaveSlotData::StaticClass(); /** If checked, will attempt to Save Game to first Slot found, timed event. */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Automatic") bool bPeriodicSave = false; /** Interval in seconds for auto saving */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings", + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Automatic", 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(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Automatic") bool bSaveOnClose = false; /** If checked, will attempt to Load Game from last Slot found, when game starts */ - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Automatic") 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 - * 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) + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") bool bStoreGameInstance = true; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") - FSEActorClassFilter ActorFilter; + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") + FSEClassFilter ActorFilter{ AActor::StaticClass() }; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") - FSEComponentClassFilter ComponentFilter; + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") + FSEClassFilter ComponentFilter{ UActorComponent::StaticClass() }; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Settings|Serialization") + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") FSEClassFilter SubsystemFilter{ USubsystem::StaticClass() }; - /** If true, will Save and Load levels when they are shown or hidden. + /** If true, will save and load sub-levels when they are shown or hidden. * This includes level streaming and world composition. */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Level Streaming") - bool bSaveAndLoadSublevels = true; + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Serialization") + bool bStoreSublevels = 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 = "Files") + bool bUseCompression = true; /** Serialization will be multi-threaded between all available cores. */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Async") 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") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Async") ESEAsyncMode FrameSplittedSerialization = ESEAsyncMode::SaveAndLoadSync; /** Max milliseconds to use every frame in an asynchronous operation. @@ -127,25 +127,26 @@ 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 = "Async", + meta = (UIMin = "3", UIMax = "10", EditCondition="FrameSplittedSerialization != ESEAsyncMode::SaveAndLoadSync")) float MaxFrameMs = 5.f; /** Files will be loaded or saved on a secondary thread while game continues */ - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Settings|Async") + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Async") ESEAsyncMode MultithreadedFiles = ESEAsyncMode::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) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "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, + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Debug", AdvancedDisplay, meta = (EditCondition = "bDebug")) bool bDebugInScreen = true; From 7c18d6a3edeed6ba7520b449be26dba1a9293769 Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 6 Oct 2023 18:54:16 +0200 Subject: [PATCH 20/39] Cleaned serialization functions --- Source/SaveExtension/Private/LevelFilter.cpp | 49 ----- .../Private/Serialization/Records.cpp | 207 ++++++++++++++++++ .../Private/Serialization/SEDataTask_Load.cpp | 102 +-------- .../Serialization/SEDataTask_LoadLevel.cpp | 2 +- .../Private/Serialization/SEDataTask_Save.cpp | 99 +-------- Source/SaveExtension/Public/LevelFilter.h | 18 -- .../Public/Serialization/Records.h | 32 ++- .../Public/Serialization/SEDataTask_Load.h | 6 - 8 files changed, 241 insertions(+), 274 deletions(-) diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index 53c5f85..eb5314a 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -8,11 +8,6 @@ ///////////////////////////////////////////////////// // USaveDataTask -const FName FSELevelFilter::TagNoTransform{"!SaveTransform"}; -const FName FSELevelFilter::TagNoPhysics{"!SavePhysics"}; -const FName FSELevelFilter::TagNoTags{"!SaveTags"}; -const FName FSELevelFilter::TagTransform{"SaveTransform"}; - void FSELevelFilter::BakeAllowedClasses() const { @@ -34,47 +29,3 @@ bool FSELevelFilter::Stores(const UActorComponent* Component) const { return ComponentFilter.IsAllowed(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/Serialization/Records.cpp b/Source/SaveExtension/Private/Serialization/Records.cpp index 797d56e..1b472f5 100644 --- a/Source/SaveExtension/Private/Serialization/Records.cpp +++ b/Source/SaveExtension/Private/Serialization/Records.cpp @@ -2,7 +2,11 @@ #include "Serialization/Records.h" +#include "SaveExtension.h" +#include "LevelFilter.h" #include "SaveSlotData.h" +#include "Serialization/SEArchive.h" + ///////////////////////////////////////////////////// @@ -75,3 +79,206 @@ bool FActorRecord::Serialize(FArchive& Ar) Ar << ComponentRecords; return true; } + + +const FName SERecords::TagNoTransform{"!SaveTransform"}; +const FName SERecords::TagNoPhysics{"!SavePhysics"}; +const FName SERecords::TagNoTags{"!SaveTags"}; + + +void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor); + + Record = FActorRecord{Actor}; + + Record.bHiddenInGame = Actor->IsHidden(); + Record.bIsProcedural = Actor->HasAnyFlags(RF_WasLoaded | RF_LoadCompleted); + + if (StoresTags(Actor)) + { + Record.Tags = Actor->Tags; + } + else // only save save-tags + { + for (const auto& Tag : Actor->Tags) + { + if (IsSaveTag(Tag)) + { + Record.Tags.Add(Tag); + } + } + } + + if (StoresTransform(Actor)) + { + Record.Transform = Actor->GetTransform(); + if (StoresPhysics(Actor)) + { + USceneComponent* const Root = Actor->GetRootComponent(); + if (Root && Root->Mobility == EComponentMobility::Movable) + { + if (auto* const Primitive = Cast(Root)) + { + Record.LinearVelocity = Primitive->GetPhysicsLinearVelocity(); + Record.AngularVelocity = Primitive->GetPhysicsAngularVelocityInRadians(); + } + else + { + Record.LinearVelocity = Root->GetComponentVelocity(); + } + } + } + } + + if (Filter.StoresAnyComponents()) + { + for (auto* Component : Actor->GetComponents()) + { + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor | Component); + if (IsValid(Component) && Filter.Stores(Component)) + { + FComponentRecord& ComponentRecord = Record.ComponentRecords.Add_GetRef({Component}); + if (const auto* SceneComp = Cast(Component)) + { + if (SceneComp->Mobility == EComponentMobility::Movable) + { + ComponentRecord.Transform = SceneComp->GetRelativeTransform(); + } + } + + if (StoresTags(Component)) + { + ComponentRecord.Tags = Component->ComponentTags; + } + + if (Component->GetClass()->IsChildOf()) + { + continue; + } + + FMemoryWriter MemoryWriter(ComponentRecord.Data, true); + FSEArchive Archive(MemoryWriter, false); + Component->Serialize(Archive); + } + } + } + + TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor | Serialize); + FMemoryWriter MemoryWriter(Record.Data, true); + FSEArchive Archive(MemoryWriter, false); + const_cast(Actor)->Serialize(Archive); +} + +bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter) +{ + TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor); + + if (Actor->GetClass() != Record.Class) + { + UE_LOG( + LogSaveExtension, Log, TEXT("Actor '{}' exists but class doesn't match"), Record.Name); + return false; + } + + Actor->Tags = Record.Tags; + + if (StoresTransform(Actor)) + { + Actor->SetActorTransform(Record.Transform); + if (StoresPhysics(Actor)) + { + USceneComponent* Root = Actor->GetRootComponent(); + if (auto* Primitive = Cast(Root)) + { + Primitive->SetPhysicsLinearVelocity(Record.LinearVelocity); + Primitive->SetPhysicsAngularVelocityInRadians(Record.AngularVelocity); + } + else + { + Root->ComponentVelocity = Record.LinearVelocity; + } + } + } + + Actor->SetActorHiddenInGame(Record.bHiddenInGame); + + TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents); + + if (Filter.StoresAnyComponents()) + { + for (auto* Component : Actor->GetComponents()) + { + TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor | Component); + if (!IsValid(Component) || !Filter.Stores(Component)) + { + continue; + } + + // Find the record + const FComponentRecord* ComponentRecord = Record.ComponentRecords.FindByKey(Component); + if (!ComponentRecord) + { + continue; // Record not found. + } + + if (USceneComponent* SceneComp = Cast(Component)) + { + if (SceneComp->Mobility == EComponentMobility::Movable) + { + SceneComp->SetRelativeTransform(ComponentRecord->Transform); + } + } + + Component->ComponentTags = ComponentRecord->Tags; + + if (!Component->GetClass()->IsChildOf()) + { + FMemoryReader MemoryReader(ComponentRecord->Data, true); + FSEArchive Archive(MemoryReader, false); + Component->Serialize(Archive); + } + } + } + + TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor | Deserialize); + FMemoryReader MemoryReader(Record.Data, true); + FSEArchive Archive(MemoryReader, false); + Actor->Serialize(Archive); + return true; +} + +void SERecords::SerializePlayer(const APlayerState* PlayerState, FActorRecord& Record) {} + +void SERecords::DeserializePlayer(APlayerState* PlayerState, const FActorRecord& Record) {} + + +bool SERecords::IsSaveTag(const FName& Tag) +{ + return Tag == TagNoTransform || Tag == TagNoPhysics || Tag == TagNoTags; +} + +bool SERecords::StoresTransform(const AActor* Actor) +{ + return Actor->IsRootComponentMovable() && !Actor->ActorHasTag(TagNoTransform); +} + +bool SERecords::StoresPhysics(const AActor* Actor) +{ + return !Actor->ActorHasTag(TagNoPhysics); +} + +bool SERecords::StoresTags(const AActor* Actor) +{ + return !Actor->ActorHasTag(TagNoTags); +} + +bool SERecords::IsProcedural(const AActor* Actor) +{ + return Actor->HasAnyFlags(RF_WasLoaded | RF_LoadCompleted); +} + +bool SERecords::StoresTags(const UActorComponent* Component) +{ + return !Component->ComponentHasTag(TagNoTags); +} diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index febdde4..6723640 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -295,7 +295,7 @@ void FSEDataTask_Load::DeserializeLevelSync(const ULevel* Level, const ULevelStr const FActorRecord* Record = RecordToActor.Key; AActor* Actor = RecordToActor.Value.Get(); check(Record && Actor); - DeserializeActor(Actor, *Record, LevelRecord); + SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter); } } @@ -355,7 +355,7 @@ void FSEDataTask_Load::DeserializeASyncLoop(float StartMS) { continue; } - DeserializeActor(Actor, *Record, LevelRecord); + SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter); const float CurrentMS = GetTimeMilliseconds(); if (CurrentMS - StartMS >= MaxFrameMs) @@ -496,104 +496,6 @@ void FSEDataTask_Load::RespawnActors( } } -bool FSEDataTask_Load::DeserializeActor( - AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord) -{ - 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 = ActorRecord.Tags; - - const bool bSavesPhysics = FSELevelFilter::StoresPhysics(Actor); - if (FSELevelFilter::StoresTransform(Actor)) - { - Actor->SetActorTransform(ActorRecord.Transform); - - if (FSELevelFilter::StoresPhysics(Actor)) - { - USceneComponent* Root = Actor->GetRootComponent(); - if (auto* Primitive = Cast(Root)) - { - Primitive->SetPhysicsLinearVelocity(ActorRecord.LinearVelocity); - Primitive->SetPhysicsAngularVelocityInRadians(ActorRecord.AngularVelocity); - } - else - { - Root->ComponentVelocity = ActorRecord.LinearVelocity; - } - } - } - - Actor->SetActorHiddenInGame(ActorRecord.bHiddenInGame); - - DeserializeActorComponents(Actor, ActorRecord, LevelRecord, 2); - - { - // Serialize from Record Data - FMemoryReader MemoryReader(ActorRecord.Data, true); - FSEArchive Archive(MemoryReader, false); - Actor->Serialize(Archive); - } - - return true; -} - -void FSEDataTask_Load::DeserializeActorComponents( - AActor* Actor, const FActorRecord& ActorRecord, const FLevelRecord& LevelRecord, int8 Indent) -{ - TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents); - - if (!LevelRecord.Filter.StoresAnyComponents()) - { - return; - } - - for (auto* Component : Actor->GetComponents()) - { - if (!IsValid(Component) || !LevelRecord.Filter.Stores(Component)) - { - 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)) - { - USceneComponent* Scene = CastChecked(Component); - if (Scene->Mobility == EComponentMobility::Movable) - { - Scene->SetRelativeTransform(Record->Transform); - } - } - - if (FSELevelFilter::StoresTags(Component)) - { - Component->ComponentTags = Record->Tags; - } - - if (!Component->GetClass()->IsChildOf()) - { - FMemoryReader MemoryReader(Record->Data, true); - FSEArchive Archive(MemoryReader, false); - Component->Serialize(Archive); - } - } -} - 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 index 3697713..af644af 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp @@ -59,7 +59,7 @@ void FSEDataTask_LoadLevel::DeserializeASyncLoop(float StartMS /*= 0.0f*/) { continue; } - DeserializeActor(Actor, *Record, LevelRecord); + SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter); const float CurrentMS = GetTimeMilliseconds(); if (CurrentMS - StartMS >= MaxFrameMs) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 7dba960..603302a 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -16,103 +16,6 @@ #include -void SerializeActorComponents( - const AActor* Actor, FActorRecord& ActorRecord, const FSELevelFilter& Filter) -{ - TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActorComponents); - - const TSet& Components = Actor->GetComponents(); - for (auto* Component : Components) - { - TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActorComponents | Component); - if (IsValid(Component) && Filter.Stores(Component)) - { - FComponentRecord& ComponentRecord = ActorRecord.ComponentRecords.Add_GetRef({Component}); - - if (Filter.StoresTransform(Component)) - { - const USceneComponent* Scene = CastChecked(Component); - if (Scene->Mobility == EComponentMobility::Movable) - { - ComponentRecord.Transform = Scene->GetRelativeTransform(); - } - } - - if (Filter.StoresTags(Component)) - { - ComponentRecord.Tags = Component->ComponentTags; - } - - if (!Component->GetClass()->IsChildOf()) - { - FMemoryWriter MemoryWriter(ComponentRecord.Data, true); - FSEArchive Archive(MemoryWriter, false); - Component->Serialize(Archive); - } - } - } -} - -bool SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter) -{ - TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor); - - Record = FActorRecord{Actor}; - - Record.bHiddenInGame = Actor->IsHidden(); - Record.bIsProcedural = Filter.IsProcedural(Actor); - - if (Filter.StoresTags(Actor)) - { - Record.Tags = Actor->Tags; - } - else - { - // Only save save-tags - for (const auto& Tag : Actor->Tags) - { - if (Filter.IsSaveTag(Tag)) - { - Record.Tags.Add(Tag); - } - } - } - - if (Filter.StoresTransform(Actor)) - { - Record.Transform = Actor->GetTransform(); - - if (Filter.StoresPhysics(Actor)) - { - USceneComponent* const Root = Actor->GetRootComponent(); - if (Root && Root->Mobility == EComponentMobility::Movable) - { - if (auto* const Primitive = Cast(Root)) - { - Record.LinearVelocity = Primitive->GetPhysicsLinearVelocity(); - Record.AngularVelocity = Primitive->GetPhysicsAngularVelocityInRadians(); - } - else - { - Record.LinearVelocity = Root->GetComponentVelocity(); - } - } - } - } - - if (Filter.StoresAnyComponents()) - { - SerializeActorComponents(Actor, Record, Filter); - } - - TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor | Serialize); - FMemoryWriter MemoryWriter(Record.Data, true); - FSEArchive Archive(MemoryWriter, false); - const_cast(Actor)->Serialize(Archive); - return true; -} - - ///////////////////////////////////////////////////// // FSEDataTask_Save @@ -368,7 +271,7 @@ void FSEDataTask_Save::SerializeLevel( ParallelFor(ActorsToSerialize.Num(), [&LevelRecord, &ActorsToSerialize, &Filter](int32 i) { - SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter); + SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter); }, Slot->ShouldSerializeAsync()? EParallelForFlags::None : EParallelForFlags::ForceSingleThread); } diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 73d6413..0b73f87 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -20,11 +20,6 @@ struct FSELevelFilter { GENERATED_BODY() - static const FName TagNoTransform; - static const FName TagNoPhysics; - static const FName TagNoTags; - static const FName TagTransform; - public: UPROPERTY(SaveGame) FSEClassFilter ActorFilter; @@ -40,17 +35,4 @@ struct FSELevelFilter 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); - - 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/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index 67b67fc..6845d9d 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -1,6 +1,5 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. - #pragma once #include @@ -10,6 +9,7 @@ #include "Records.generated.h" +struct FSELevelFilter; class USaveSlotData; @@ -121,4 +121,32 @@ struct FSubsystemRecord : public FObjectRecord FSubsystemRecord() : Super() {} FSubsystemRecord(const USubsystem* Subsystem) : Super(Subsystem) {} -}; \ No newline at end of file +}; + +USTRUCT() +struct FControlledRecord +{ + GENERATED_BODY() + +}; + + +namespace SERecords +{ + extern const FName TagNoTransform; + extern const FName TagNoPhysics; + extern const FName TagNoTags; + + + void SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter); + bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter); + void SerializePlayer(const APlayerState* PlayerState, FActorRecord& Record); + void DeserializePlayer(APlayerState* PlayerState, const FActorRecord& Record); + + bool IsSaveTag(const FName& Tag); + bool StoresTransform(const AActor* Actor); + bool StoresPhysics(const AActor* Actor); + bool StoresTags(const AActor* Actor); + bool IsProcedural(const AActor* Actor); + bool StoresTags(const UActorComponent* Component); +} diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h index d67659b..1f9f9df 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask_Load.h @@ -112,11 +112,5 @@ struct FSEDataTask_Load : public FSEDataTask void PrepareLevel(const ULevel* Level, FLevelRecord& LevelRecord); void FindNextAsyncLevel(ULevelStreaming*& OutLevelStreaming) const; - - /** Serializes an actor into this Actor Record */ - 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 FLevelRecord& LevelRecord, int8 indent = 0); /** END Deserialization */ }; \ No newline at end of file From f95e5a1c374f0be7d848a0b227d4422adb0fad9e Mon Sep 17 00:00:00 2001 From: muit Date: Fri, 6 Oct 2023 20:28:18 +0200 Subject: [PATCH 21/39] Fixed level filter pin --- Source/SaveExtension/Private/SaveSlot.cpp | 19 +++++++++++-------- Source/SaveExtension/Public/ClassFilter.h | 4 ---- Source/SaveExtension/Public/LevelFilter.h | 11 +++++++---- Source/SaveExtension/Public/SaveSlot.h | 15 +++++++++------ 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index b9a4995..2cc6be5 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -15,7 +15,6 @@ void USaveSlot::PostInitProperties() { Super::PostInitProperties(); - Data = NewObject(this, DataClass, TEXT("SlotData")); } void USaveSlot::Serialize(FArchive& Ar) @@ -34,11 +33,13 @@ void USaveSlot::Serialize(FArchive& Ar) } else { - uint8* MipData = static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY)); - check( MipData != nullptr ); + uint8* MipData = + static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_ONLY)); + check(MipData != nullptr); int64 MipDataSize = Thumbnail->GetPlatformData()->Mips[0].BulkData.GetBulkDataSize(); - FImageView MipImage(MipData, Thumbnail->GetPlatformData()->SizeX,Thumbnail->GetPlatformData()->SizeY, 1, ERawImageFormat::BGRA8, EGammaSpace::sRGB); + FImageView MipImage(MipData, Thumbnail->GetPlatformData()->SizeX, + Thumbnail->GetPlatformData()->SizeY, 1, ERawImageFormat::BGRA8, EGammaSpace::sRGB); FImageUtils::CompressImage(ThumbnailData, TEXT("PNG"), MipImage); Thumbnail->GetPlatformData()->Mips[0].BulkData.Unlock(); Ar << ThumbnailData; @@ -46,7 +47,8 @@ void USaveSlot::Serialize(FArchive& Ar) } } -void USaveSlot::CaptureThumbnail(FSEOnThumbnailCaptured Callback, const int32 Width /*= 640*/, const int32 Height /*= 360*/) +void USaveSlot::CaptureThumbnail( + FSEOnThumbnailCaptured Callback, const int32 Width /*= 640*/, const int32 Height /*= 360*/) { if (!GEngine || bCapturingThumbnail) { @@ -54,7 +56,7 @@ void USaveSlot::CaptureThumbnail(FSEOnThumbnailCaptured Callback, const int32 Wi return; } - auto* Viewport = GEngine->GameViewport? GEngine->GameViewport->Viewport : nullptr; + auto* Viewport = GEngine->GameViewport ? GEngine->GameViewport->Viewport : nullptr; if (!Viewport) { Callback.ExecuteIfBound(false); @@ -148,8 +150,9 @@ void USaveSlot::OnThumbnailCaptured(int32 InSizeX, int32 InSizeY, const TArrayDeferCompression = true; #endif - FColor* TextureData = static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE)); - for(int32 i = 0; i < InImageData.Num(); ++i, ++TextureData) + FColor* TextureData = + static_cast(Thumbnail->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE)); + for (int32 i = 0; i < InImageData.Num(); ++i, ++TextureData) { *TextureData = InImageData[i]; } diff --git a/Source/SaveExtension/Public/ClassFilter.h b/Source/SaveExtension/Public/ClassFilter.h index 1db6624..55b6c06 100644 --- a/Source/SaveExtension/Public/ClassFilter.h +++ b/Source/SaveExtension/Public/ClassFilter.h @@ -2,13 +2,9 @@ #pragma once -#include -#include - #include "ClassFilter.generated.h" - USTRUCT(BlueprintType) struct SAVEEXTENSION_API FSEClassFilter { diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 0b73f87..6e4c64e 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -4,6 +4,9 @@ #include "ClassFilter.h" +#include +#include + #include "LevelFilter.generated.h" @@ -21,11 +24,11 @@ struct FSELevelFilter GENERATED_BODY() public: - UPROPERTY(SaveGame) - FSEClassFilter ActorFilter; + UPROPERTY(SaveGame, BlueprintReadWrite) + FSEClassFilter ActorFilter{AActor::StaticClass()}; - UPROPERTY(SaveGame) - FSEClassFilter ComponentFilter; + UPROPERTY(SaveGame, BlueprintReadWrite) + FSEClassFilter ComponentFilter{UActorComponent::StaticClass()}; FSELevelFilter() = default; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 729699d..12aa87b 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -4,11 +4,14 @@ #include "SaveSlotData.h" +#include #include +#include #include #include "SaveSlot.generated.h" + struct FSELevelFilter; class UTexture2D; @@ -90,13 +93,13 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame bool bStoreGameInstance = true; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") - FSEClassFilter ActorFilter{ AActor::StaticClass() }; + FSEClassFilter ActorFilter{AActor::StaticClass()}; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") - FSEClassFilter ComponentFilter{ UActorComponent::StaticClass() }; + FSEClassFilter ComponentFilter{UActorComponent::StaticClass()}; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Serialization") - FSEClassFilter SubsystemFilter{ USubsystem::StaticClass() }; + FSEClassFilter SubsystemFilter{USubsystem::StaticClass()}; /** If true, will save and load sub-levels when they are shown or hidden. * This includes level streaming and world composition. @@ -128,7 +131,8 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame * non multi-threaded platforms */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Async", - meta = (UIMin = "3", UIMax = "10", EditCondition="FrameSplittedSerialization != ESEAsyncMode::SaveAndLoadSync")) + meta = (UIMin = "3", UIMax = "10", + EditCondition = "FrameSplittedSerialization != ESEAsyncMode::SaveAndLoadSync")) float MaxFrameMs = 5.f; /** Files will be loaded or saved on a secondary thread while game continues */ @@ -168,11 +172,10 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) FSaveSlotStats Stats; - UPROPERTY(BlueprintReadWrite, Transient) // Saved + UPROPERTY(BlueprintReadWrite, Transient) // Saved TObjectPtr Thumbnail; protected: - UPROPERTY(Transient) bool bCapturingThumbnail = false; FSEOnThumbnailCaptured CapturedThumbnailDelegate; From 4a9c51861d79192194f1aa573378a4dab578b7f5 Mon Sep 17 00:00:00 2001 From: muit Date: Mon, 9 Oct 2023 13:21:02 +0200 Subject: [PATCH 22/39] Added player records and serialization --- Source/SaveExtension/Private/LevelFilter.cpp | 4 -- Source/SaveExtension/Private/SaveSlot.cpp | 9 +++ Source/SaveExtension/Private/SaveSlotData.cpp | 34 +++++++++++ .../Private/Serialization/Records.cpp | 56 ++++++++++++++++--- .../Private/Serialization/SEDataTask_Load.cpp | 4 +- .../Serialization/SEDataTask_LoadLevel.cpp | 2 +- .../Private/Serialization/SEDataTask_Save.cpp | 7 +-- Source/SaveExtension/Public/LevelFilter.h | 1 - Source/SaveExtension/Public/SaveSlot.h | 4 ++ Source/SaveExtension/Public/SaveSlotData.h | 15 ++++- .../Public/Serialization/Records.h | 28 +++++++--- 11 files changed, 132 insertions(+), 32 deletions(-) diff --git a/Source/SaveExtension/Private/LevelFilter.cpp b/Source/SaveExtension/Private/LevelFilter.cpp index eb5314a..9ec288c 100644 --- a/Source/SaveExtension/Private/LevelFilter.cpp +++ b/Source/SaveExtension/Private/LevelFilter.cpp @@ -21,10 +21,6 @@ bool FSELevelFilter::Stores(const AActor* Actor) const return ActorFilter.IsAllowed(Actor->GetClass()); } -bool FSELevelFilter::StoresAnyComponents() const -{ - return ComponentFilter.IsAnyAllowed(); -} bool FSELevelFilter::Stores(const UActorComponent* Component) const { return ComponentFilter.IsAllowed(Component->GetClass()); diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 2cc6be5..1d0cfce 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -81,6 +81,15 @@ void USaveSlot::CaptureThumbnail( } } +USaveSlotData* USaveSlot::AssureData() +{ + if (!IsValid(Data)) + { + AssignData(NewObject(this, DataClass, NAME_None)); + } + return Data; +} + bool USaveSlot::ShouldDeserializeAsync() const { return MultithreadedSerialization == ESEAsyncMode::LoadAsync || diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index 0a54930..d7555ab 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -28,3 +28,37 @@ void USaveSlotData::CleanRecords(bool bKeepSublevels) SubLevels.Empty(); } } + +FPlayerRecord& USaveSlotData::FindOrAddPlayerRecord(const FUniqueNetIdRepl& UniqueId) +{ + return Players[Players.AddUnique({UniqueId})]; +} + +FPlayerRecord* USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId) +{ + const int32 Index = Players.IndexOfByPredicate([&UniqueId](const FPlayerRecord& Record) { + return Record.UniqueId == UniqueId; + }); + if (Index != INDEX_NONE) + { + return &Players[Index]; + } + return nullptr; +} + +bool USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, UPARAM(Ref) FPlayerRecord& Record) +{ + if (FPlayerRecord* FoundRecord = FindPlayerRecord(UniqueId)) + { + Record = *FoundRecord; + return true; + } + return false; +} + +bool USaveSlotData::RemovePlayerRecord(const FUniqueNetIdRepl& UniqueId) +{ + return Players.RemoveAll([&UniqueId](const FPlayerRecord& Record){ + return Record.UniqueId == UniqueId; + }) > 0; +} diff --git a/Source/SaveExtension/Private/Serialization/Records.cpp b/Source/SaveExtension/Private/Serialization/Records.cpp index 1b472f5..f2d33f7 100644 --- a/Source/SaveExtension/Private/Serialization/Records.cpp +++ b/Source/SaveExtension/Private/Serialization/Records.cpp @@ -3,10 +3,14 @@ #include "Serialization/Records.h" #include "SaveExtension.h" -#include "LevelFilter.h" +#include "ClassFilter.h" #include "SaveSlotData.h" #include "Serialization/SEArchive.h" +#include +#include +#include + ///////////////////////////////////////////////////// @@ -86,7 +90,7 @@ const FName SERecords::TagNoPhysics{"!SavePhysics"}; const FName SERecords::TagNoTags{"!SaveTags"}; -void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter) +void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter) { TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor); @@ -131,12 +135,12 @@ void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const } } - if (Filter.StoresAnyComponents()) + if (ComponentFilter.IsAnyAllowed()) { for (auto* Component : Actor->GetComponents()) { TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor | Component); - if (IsValid(Component) && Filter.Stores(Component)) + if (IsValid(Component) && ComponentFilter.IsAllowed(Component->GetClass())) { FComponentRecord& ComponentRecord = Record.ComponentRecords.Add_GetRef({Component}); if (const auto* SceneComp = Cast(Component)) @@ -170,7 +174,7 @@ void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const const_cast(Actor)->Serialize(Archive); } -bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter) +bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter) { TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor); @@ -205,12 +209,12 @@ bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, cons TRACE_CPUPROFILER_EVENT_SCOPE(UFSEDataTask_Load::DeserializeActorComponents); - if (Filter.StoresAnyComponents()) + if (ComponentFilter.IsAnyAllowed()) { for (auto* Component : Actor->GetComponents()) { TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor | Component); - if (!IsValid(Component) || !Filter.Stores(Component)) + if (!IsValid(Component) || !ComponentFilter.IsAllowed(Component->GetClass())) { continue; } @@ -248,9 +252,43 @@ bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, cons return true; } -void SERecords::SerializePlayer(const APlayerState* PlayerState, FActorRecord& Record) {} +void SERecords::SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter) +{ + check(PlayerState); + + APlayerController* PC = PlayerState->GetPlayerController(); + APawn* Pawn = PlayerState->GetPawn(); + + Record.UniqueId = PlayerState->GetUniqueId(); + SERecords::SerializeActor(PlayerState, Record.PlayerState, ComponentFilter); + if (Pawn) + { + SERecords::SerializeActor(Pawn, Record.Pawn, ComponentFilter); + } + if (PC) + { + SERecords::SerializeActor(PC, Record.Controller, ComponentFilter); + } +} -void SERecords::DeserializePlayer(APlayerState* PlayerState, const FActorRecord& Record) {} +void SERecords::DeserializePlayer(APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter) +{ + check(PlayerState); + check(PlayerState->GetUniqueId() == Record.UniqueId); + + APlayerController* PC = PlayerState->GetPlayerController(); + APawn* Pawn = PlayerState->GetPawn(); + + SERecords::DeserializeActor(PlayerState, Record.PlayerState, ComponentFilter); + if (Pawn) + { + SERecords::DeserializeActor(Pawn, Record.Pawn, ComponentFilter); + } + if (PC) + { + SERecords::DeserializeActor(PC, Record.Controller, ComponentFilter); + } +} bool SERecords::IsSaveTag(const FName& Tag) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp index 6723640..1b0e6fc 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Load.cpp @@ -295,7 +295,7 @@ void FSEDataTask_Load::DeserializeLevelSync(const ULevel* Level, const ULevelStr const FActorRecord* Record = RecordToActor.Key; AActor* Actor = RecordToActor.Value.Get(); check(Record && Actor); - SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter); + SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter.ComponentFilter); } } @@ -355,7 +355,7 @@ void FSEDataTask_Load::DeserializeASyncLoop(float StartMS) { continue; } - SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter); + SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter.ComponentFilter); const float CurrentMS = GetTimeMilliseconds(); if (CurrentMS - StartMS >= MaxFrameMs) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp index af644af..5d0174a 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_LoadLevel.cpp @@ -59,7 +59,7 @@ void FSEDataTask_LoadLevel::DeserializeASyncLoop(float StartMS /*= 0.0f*/) { continue; } - SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter); + SERecords::DeserializeActor(Actor, *Record, LevelRecord.Filter.ComponentFilter); const float CurrentMS = GetTimeMilliseconds(); if (CurrentMS - StartMS >= MaxFrameMs) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 603302a..38c5000 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -21,7 +21,7 @@ FSEDataTask_Save::FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) : FSEDataTask(Manager, ESETaskType::Save) - , SlotData(Slot->GetData()) + , SlotData(Slot->AssureData()) {} FSEDataTask_Save::~FSEDataTask_Save() @@ -68,9 +68,6 @@ void FSEDataTask_Save::OnStart() Manager->OnSaveBegan(); - Slot = Manager->GetActiveSlot(); - SlotData = Slot->GetData(); - check(SlotData->GetClass() == Slot->DataClass); SlotData->CleanRecords(true); @@ -271,7 +268,7 @@ void FSEDataTask_Save::SerializeLevel( ParallelFor(ActorsToSerialize.Num(), [&LevelRecord, &ActorsToSerialize, &Filter](int32 i) { - SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter); + SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter.ComponentFilter); }, Slot->ShouldSerializeAsync()? EParallelForFlags::None : EParallelForFlags::ForceSingleThread); } diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index 6e4c64e..ab33bae 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -36,6 +36,5 @@ struct FSELevelFilter void BakeAllowedClasses() const; bool Stores(const AActor* Actor) const; - bool StoresAnyComponents() const; bool Stores(const UActorComponent* Component) const; }; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 12aa87b..a50c03b 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -211,6 +211,10 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame Data = NewData; } + // Gets the data instance, and creates it if wasn't already + UFUNCTION(BlueprintCallable, Category = SaveSlot) + USaveSlotData* AssureData(); + bool ShouldDeserializeAsync() const; bool ShouldSerializeAsync() const; diff --git a/Source/SaveExtension/Public/SaveSlotData.h b/Source/SaveExtension/Public/SaveSlotData.h index 253341d..7a26919 100644 --- a/Source/SaveExtension/Public/SaveSlotData.h +++ b/Source/SaveExtension/Public/SaveSlotData.h @@ -14,6 +14,9 @@ #include "SaveSlotData.generated.h" +struct FUniqueNetIdRepl; + + /** * USaveSlotData stores all information that can be accessible only while the game is loaded. * Works like a common SaveGame object @@ -27,7 +30,7 @@ class SAVEEXTENSION_API USaveSlotData : public UObject public: /** Game world time since game started in seconds */ - UPROPERTY(SaveGame, Category = SaveData, BlueprintReadOnly) + UPROPERTY(SaveGame, Category = SaveSlotData, BlueprintReadOnly) float TimeSeconds; /** Records @@ -42,9 +45,19 @@ class SAVEEXTENSION_API USaveSlotData : public UObject FPersistentLevelRecord RootLevel; TArray SubLevels; + TArray Players; + void CleanRecords(bool bKeepSublevels); /** Using manual serialization. It's way faster than reflection serialization */ virtual void Serialize(FArchive& Ar) override; + + UFUNCTION(BlueprintPure, Category = SaveSlotData) + FPlayerRecord& FindOrAddPlayerRecord(const FUniqueNetIdRepl& UniqueId); + FPlayerRecord* FindPlayerRecord(const FUniqueNetIdRepl& UniqueId); + UFUNCTION(BlueprintPure, Category = SaveSlotData) + bool FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, UPARAM(Ref) FPlayerRecord& Record); + UFUNCTION(BlueprintPure, Category = SaveSlotData) + bool RemovePlayerRecord(const FUniqueNetIdRepl& UniqueId); }; diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index 6845d9d..a04167d 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -3,14 +3,13 @@ #pragma once #include -#include -#include #include "Records.generated.h" -struct FSELevelFilter; +struct FSEClassFilter; class USaveSlotData; +class APlayerState; USTRUCT() @@ -123,11 +122,22 @@ struct FSubsystemRecord : public FObjectRecord FSubsystemRecord(const USubsystem* Subsystem) : Super(Subsystem) {} }; -USTRUCT() -struct FControlledRecord +USTRUCT(BlueprintType) +struct FPlayerRecord { GENERATED_BODY() + FUniqueNetIdRepl UniqueId; + + FActorRecord PlayerState; + FActorRecord Controller; + FActorRecord Pawn; + + + bool operator==(const FPlayerRecord& Other) const + { + return UniqueId == Other.UniqueId; + } }; @@ -138,10 +148,10 @@ namespace SERecords extern const FName TagNoTags; - void SerializeActor(const AActor* Actor, FActorRecord& Record, const FSELevelFilter& Filter); - bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSELevelFilter& Filter); - void SerializePlayer(const APlayerState* PlayerState, FActorRecord& Record); - void DeserializePlayer(APlayerState* PlayerState, const FActorRecord& Record); + void SerializeActor(const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter); + bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter); + void SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter); + void DeserializePlayer(APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter); bool IsSaveTag(const FName& Tag); bool StoresTransform(const AActor* Actor); From 73fe72ed675daf438e2317f9a9215a397b72f22e Mon Sep 17 00:00:00 2001 From: muit Date: Mon, 9 Oct 2023 21:59:44 +0200 Subject: [PATCH 23/39] [CICD] Preliminar build script --- .github/workflows/build.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..61bbeb4 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,32 @@ +name: Build + +# Controls when the action will run. +on: + push: + branches: + - '**' + pull_request: + workflow_call: + +jobs: + publish: + name: ${{ matrix.os }} ${{ matrix.config }} (UE ${{ matrix.version }}) + runs-on: [self-hosted, UE-${{ matrix.version }}, ${{ matrix.os }}] + strategy: + fail-fast: false + matrix: + name: SaveExtension + config: [Debug, Release] + os: [Windows, Linux] + version: "5.3" + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Download Piperift Scripts + run: git clone https://github.com/PipeRift/CICDScripts Scripts + + - name: Build Plugin + run: py ./Scripts/build.py plugin + From 11a1bd8b81ed87b58fe85efffa629db1292b6217 Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Arce Date: Mon, 9 Oct 2023 22:24:40 +0200 Subject: [PATCH 24/39] Update build.yml --- .github/workflows/build.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61bbeb4..04d4bd3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,15 +10,16 @@ on: jobs: publish: - name: ${{ matrix.os }} ${{ matrix.config }} (UE ${{ matrix.version }}) - runs-on: [self-hosted, UE-${{ matrix.version }}, ${{ matrix.os }}] + name: "${{ matrix.os }} ${{ matrix.config }} - UE ${{ matrix.version }}" + runs-on: [self-hosted, "UE-${{ matrix.version }}", "${{ matrix.os }}"] + env: + CI_PLUGIN: SaveExtension strategy: fail-fast: false matrix: - name: SaveExtension - config: [Debug, Release] + config: [Release] os: [Windows, Linux] - version: "5.3" + version: [ "5.3" ] steps: - name: Checkout From e3280cac723e01dbd2a9c7f36c995e4346d17b9e Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Arce Date: Mon, 9 Oct 2023 22:37:54 +0200 Subject: [PATCH 25/39] [CICD] Update build.yml --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04d4bd3..78eb493 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,5 +29,7 @@ jobs: run: git clone https://github.com/PipeRift/CICDScripts Scripts - name: Build Plugin - run: py ./Scripts/build.py plugin + run: | + cd Scripts + python build.py plugin From ed5690153ee72e0d38e2c2bb900e6cdb3fcbde2d Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Arce Date: Mon, 9 Oct 2023 22:57:34 +0200 Subject: [PATCH 26/39] Update build.yml --- .github/workflows/build.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78eb493..2ddeaad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,5 @@ jobs: run: git clone https://github.com/PipeRift/CICDScripts Scripts - name: Build Plugin - run: | - cd Scripts - python build.py plugin + run: python "./Scripts/build.py" plugin -n ${{ env.CI_PLUGIN }} -p . From e3362f7a4b0c3ecafb245925950977c8498d99a8 Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Arce Date: Tue, 10 Oct 2023 00:16:15 +0200 Subject: [PATCH 27/39] [CICD] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ddeaad..dadb965 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,5 +29,5 @@ jobs: run: git clone https://github.com/PipeRift/CICDScripts Scripts - name: Build Plugin - run: python "./Scripts/build.py" plugin -n ${{ env.CI_PLUGIN }} -p . + run: python Scripts/build.py plugin -n ${{ env.CI_PLUGIN }} -p . From 02a865aa13d7d6d27c7cf855578594f618be050f Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 10 Oct 2023 00:20:42 +0200 Subject: [PATCH 28/39] Build fixes --- .gitignore | 3 +- .../ClassFilter/ClassFilterHelpers.h | 47 +++++++++---------- Source/SaveExtension/Public/LevelFilter.h | 4 +- Source/SaveExtension/Public/SaveSlot.h | 2 +- Source/Test/Private/GameInstanceSpec.cpp | 4 +- Source/Test/Private/GameInstanceSpec.h | 2 +- ...estGameInstance.h => SETestGameInstance.h} | 4 +- 7 files changed, 33 insertions(+), 33 deletions(-) rename Source/Test/Private/Helpers/{TestGameInstance.h => SETestGameInstance.h} (70%) diff --git a/.gitignore b/.gitignore index 832491c..f5f6af4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /Binaries -/Intermediate \ No newline at end of file +/Intermediate +/Build diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h index 63b26d3..f1187fa 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h @@ -1,8 +1,8 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include "ClassFilterNode.h" #include "ClassFilter.h" +#include "ClassFilterNode.h" #include #include @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -104,8 +103,8 @@ namespace ClassFilter 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 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 */ @@ -265,14 +264,14 @@ namespace ClassFilter /** 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 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. @@ -356,12 +355,12 @@ 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. @@ -393,14 +392,14 @@ namespace ClassFilter /** 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 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. @@ -475,12 +474,12 @@ namespace ClassFilter } /** 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. diff --git a/Source/SaveExtension/Public/LevelFilter.h b/Source/SaveExtension/Public/LevelFilter.h index ab33bae..647c297 100644 --- a/Source/SaveExtension/Public/LevelFilter.h +++ b/Source/SaveExtension/Public/LevelFilter.h @@ -24,10 +24,10 @@ struct FSELevelFilter GENERATED_BODY() public: - UPROPERTY(SaveGame, BlueprintReadWrite) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = LevelFilter) FSEClassFilter ActorFilter{AActor::StaticClass()}; - UPROPERTY(SaveGame, BlueprintReadWrite) + UPROPERTY(SaveGame, BlueprintReadWrite, Category = LevelFilter) FSEClassFilter ComponentFilter{UActorComponent::StaticClass()}; diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index a50c03b..8d585fe 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -172,7 +172,7 @@ class SAVEEXTENSION_API USaveSlot : public USaveGame UPROPERTY(SaveGame, BlueprintReadWrite, Category = SaveSlot) FSaveSlotStats Stats; - UPROPERTY(BlueprintReadWrite, Transient) // Saved + UPROPERTY(BlueprintReadWrite, Transient, Category = SaveSlot) // Saved TObjectPtr Thumbnail; protected: diff --git a/Source/Test/Private/GameInstanceSpec.cpp b/Source/Test/Private/GameInstanceSpec.cpp index 4f064d7..34c0888 100644 --- a/Source/Test/Private/GameInstanceSpec.cpp +++ b/Source/Test/Private/GameInstanceSpec.cpp @@ -22,7 +22,7 @@ class FSaveSpec_GameInstance : public Automatron::FTestSpec bCanUsePIEWorld = false; DefaultWorldSettings.bShouldTick = true; - DefaultWorldSettings.GameInstance = UTestGameInstance::StaticClass(); + DefaultWorldSettings.GameInstance = USETestGameInstance::StaticClass(); } }; @@ -38,7 +38,7 @@ void FSaveSpec_GameInstance::Define() }); It("GameInstance can be saved", [this]() { - auto* GI = GetMainWorld()->GetGameInstance(); + auto* GI = GetMainWorld()->GetGameInstance(); GI->bMyBool = true; SaveManager->SaveSlot(0); diff --git a/Source/Test/Private/GameInstanceSpec.h b/Source/Test/Private/GameInstanceSpec.h index afe795c..c92785a 100644 --- a/Source/Test/Private/GameInstanceSpec.h +++ b/Source/Test/Private/GameInstanceSpec.h @@ -1,7 +1,7 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. #pragma once -#include "Helpers/TestGameInstance.h" +#include "Helpers/SETestGameInstance.h" #include diff --git a/Source/Test/Private/Helpers/TestGameInstance.h b/Source/Test/Private/Helpers/SETestGameInstance.h similarity index 70% rename from Source/Test/Private/Helpers/TestGameInstance.h rename to Source/Test/Private/Helpers/SETestGameInstance.h index dfb6145..1399aee 100644 --- a/Source/Test/Private/Helpers/TestGameInstance.h +++ b/Source/Test/Private/Helpers/SETestGameInstance.h @@ -6,11 +6,11 @@ #include #include -#include "TestGameInstance.generated.h" +#include "SETestGameInstance.generated.h" UCLASS() -class UTestGameInstance : public UGameInstance +class USETestGameInstance : public UGameInstance { GENERATED_BODY() From 1dad6167e699b8b2c4f9f33f65d75aa17e4fadb4 Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Arce Date: Tue, 10 Oct 2023 00:46:53 +0200 Subject: [PATCH 29/39] [CICD] use Python 3 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dadb965..1733bc4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,5 +29,5 @@ jobs: run: git clone https://github.com/PipeRift/CICDScripts Scripts - name: Build Plugin - run: python Scripts/build.py plugin -n ${{ env.CI_PLUGIN }} -p . + run: python3 Scripts/build.py plugin -n ${{ env.CI_PLUGIN }} -p . From 8898606956f635965cd16c7cc1a636dd5ae39a56 Mon Sep 17 00:00:00 2001 From: Miguel Fernandez Arce Date: Tue, 10 Oct 2023 22:06:28 +0200 Subject: [PATCH 30/39] [CICD] Update build.yml --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1733bc4..dadb965 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,5 +29,5 @@ jobs: run: git clone https://github.com/PipeRift/CICDScripts Scripts - name: Build Plugin - run: python3 Scripts/build.py plugin -n ${{ env.CI_PLUGIN }} -p . + run: python Scripts/build.py plugin -n ${{ env.CI_PLUGIN }} -p . From 685e198063d789bdb660b0f9d5f810bf00fbcbb5 Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 11 Oct 2023 22:57:04 +0200 Subject: [PATCH 31/39] Fixes for Linux builds --- .../Private/SaveActorEditorTabSummoner.cpp | 91 +++++++------------ Source/Test/Private/Automatron.h | 65 +++++++------ 2 files changed, 67 insertions(+), 89 deletions(-) diff --git a/Source/Editor/Private/SaveActorEditorTabSummoner.cpp b/Source/Editor/Private/SaveActorEditorTabSummoner.cpp index 1051dee..7a64743 100644 --- a/Source/Editor/Private/SaveActorEditorTabSummoner.cpp +++ b/Source/Editor/Private/SaveActorEditorTabSummoner.cpp @@ -2,47 +2,39 @@ #include "SaveActorEditorTabSummoner.h" -#include -#include -#include -#include -#include #include -#include -#include +#include #include #include +#include +#include +#include +#include #include +#include #define LOCTEXT_NAMESPACE "SaveActorEditorSummoner" -const TArray SSaveActorEditorWidget::TagList -{ - { TEXT("Save Tags"), TEXT("!SaveTags"), true, LOCTEXT("tags_tooltip", "Should save tags?") }, - { TEXT("Save Transform"), TEXT("!SaveTransform"), true, LOCTEXT("transform_tooltip", "Should save position, rotation and scale?") }, - { TEXT("Save Physics"), TEXT("!SavePhysics"), true, LOCTEXT("physics_tooltip", "Should save physics?") } -}; +const TArray SSaveActorEditorWidget::TagList{ + {TEXT("Save Tags"), TEXT("!SaveTags"), true, LOCTEXT("tags_tooltip", "Should save tags?")}, + {TEXT("Save Transform"), TEXT("!SaveTransform"), true, + LOCTEXT("transform_tooltip", "Should save position, rotation and scale?")}, + {TEXT("Save Physics"), TEXT("!SavePhysics"), true, LOCTEXT("physics_tooltip", "Should save physics?")}}; void SSaveActorEditorWidget::Construct(const FArguments&, TWeakPtr InBlueprintEditor) { bRefreshingVisuals = false; - OnBlueprintPreCompileHandle = GEditor->OnBlueprintPreCompile().AddSP(this, &SSaveActorEditorWidget::OnBlueprintPreCompile); - OnObjectPreSaveHandle = FCoreUObjectDelegates::OnObjectPreSave.AddSP(this, &SSaveActorEditorWidget::OnObjectPreSave); + OnBlueprintPreCompileHandle = + GEditor->OnBlueprintPreCompile().AddSP(this, &SSaveActorEditorWidget::OnBlueprintPreCompile); + OnObjectPreSaveHandle = + FCoreUObjectDelegates::OnObjectPreSave.AddSP(this, &SSaveActorEditorWidget::OnObjectPreSave); WeakBlueprintEditor = InBlueprintEditor; - ChildSlot - .Padding(10) - [ - SNew(SVerticalBox) - .Visibility(this, &SSaveActorEditorWidget::GetContentVisibility) - + SVerticalBox::Slot() - .AutoHeight().Padding(2) - [ - GenerateSettingsWidget().ToSharedRef() - ] - ]; + ChildSlot.Padding( + 10)[SNew(SVerticalBox).Visibility(this, &SSaveActorEditorWidget::GetContentVisibility) + + SVerticalBox::Slot().AutoHeight().Padding(2)[GenerateSettingsWidget().ToSharedRef()]]; RefreshVisuals(); } @@ -116,40 +108,27 @@ AActor* SSaveActorEditorWidget::GetDefaultActor() const TSharedPtr SSaveActorEditorWidget::GenerateSettingsWidget() { // TODO: This can be moved into FTagInfo - SettingItems.Add(SNew(SSaveActorSettingsItem).TagInfo(TagList[0]).OnValueChanged(this, &SSaveActorEditorWidget::OnSettingChanged)); - SettingItems.Add(SNew(SSaveActorSettingsItem).TagInfo(TagList[1]).OnValueChanged(this, &SSaveActorEditorWidget::OnSettingChanged)); - SettingItems.Add(SNew(SSaveActorSettingsItem).TagInfo(TagList[2]).OnValueChanged(this, &SSaveActorEditorWidget::OnSettingChanged)); + SettingItems.Add(SNew(SSaveActorSettingsItem) + .TagInfo(TagList[0]) + .OnValueChanged(this, &SSaveActorEditorWidget::OnSettingChanged)); + SettingItems.Add(SNew(SSaveActorSettingsItem) + .TagInfo(TagList[1]) + .OnValueChanged(this, &SSaveActorEditorWidget::OnSettingChanged)); + SettingItems.Add(SNew(SSaveActorSettingsItem) + .TagInfo(TagList[2]) + .OnValueChanged(this, &SSaveActorEditorWidget::OnSettingChanged)); AActor* Actor = GetDefaultActor(); if (Actor) { return SNew(SBorder) - .BorderImage(FAppStyle::GetBrush("DetailsView.CategoryMiddle")) - .Padding(0) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - [ - SettingItems[0] - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SettingItems[1] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(6) - [ - SNew(SBorder) - .BorderImage(FAppStyle::GetBrush("DetailsView.CategoryMiddle")) - .Padding(0) - [ - SettingItems[2] - ] - ] - ]; + .BorderImage(FAppStyle::GetBrush("DetailsView.CategoryMiddle")) + .Padding(0)[SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight()[SettingItems[0]] + + SVerticalBox::Slot().AutoHeight()[SettingItems[1]] + + SVerticalBox::Slot().AutoHeight().Padding( + 6)[SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("DetailsView.CategoryMiddle")) + .Padding(0)[SettingItems[2]]]]; } return SNullWidget::NullWidget; } @@ -175,7 +154,7 @@ void SSaveActorEditorWidget::RefreshVisuals() } } -const FTabId FSaveActorEditorSummoner::TabName {FName("SaveActorTab") }; +const FTabId FSaveActorEditorSummoner::TabName{FName("SaveActorTab")}; FSaveActorEditorSummoner::FSaveActorEditorSummoner(TSharedPtr BlueprintEditor) : FWorkflowTabFactory(TabName.TabType, BlueprintEditor) diff --git a/Source/Test/Private/Automatron.h b/Source/Test/Private/Automatron.h index 98afce1..7f2d967 100644 --- a/Source/Test/Private/Automatron.h +++ b/Source/Test/Private/Automatron.h @@ -38,8 +38,8 @@ #pragma once #include -#include #include +#include #include #include #include @@ -88,7 +88,6 @@ namespace Automatron class TRegister : public FRegister { public: - // Just by existing, this instance will define the class and register the spec static TRegister Instance; @@ -99,7 +98,6 @@ namespace Automatron } private: - static void Setup() { static T Spec{}; @@ -631,9 +629,10 @@ namespace Automatron uint32 Flags = 0; bool bInitializedWorld = false; -# if WITH_EDITOR +#if WITH_EDITOR bool bInitializedPIE = false; -# endif + FDelegateHandle PIEStartedHandle; +#endif TWeakObjectPtr MainWorld; @@ -731,28 +730,28 @@ namespace Automatron // GENERATION MACROS #define GENERATE_SPEC(TClass, PrettyName, TFlags) \ - GENERATE_SPEC_PRIVATE(TClass, PrettyName, TFlags, __FILE__, __LINE__) + GENERATE_SPEC_PRIVATE(TClass, PrettyName, TFlags, __FILE__, __LINE__) #define GENERATE_SPEC_PRIVATE(TClass, PrettyName, TFlags, FileName, LineNumber) \ - private: \ - void Setup() \ - { \ - FTestSpec::Setup(TEXT(#TClass), TEXT(PrettyName), FileName, LineNumber); \ - } \ - static Automatron::Spec::TRegister& __meta_register() \ - { \ - return Automatron::Spec::TRegister::Instance; \ - } \ - friend Automatron::Spec::TRegister; \ - \ - virtual void Define() override +private: \ + void Setup() \ + { \ + FTestSpec::Setup(TEXT(#TClass), TEXT(PrettyName), FileName, LineNumber); \ + } \ + static Automatron::Spec::TRegister& __meta_register() \ + { \ + return Automatron::Spec::TRegister::Instance; \ + } \ + friend Automatron::Spec::TRegister; \ + \ + virtual void Define() override #define SPEC(TClass, TParent, PrettyName, TFlags) \ - class TClass : public TParent \ - { \ - GENERATE_SPEC(TClass, PrettyName, TFlags); \ - }; \ - void TClass::Define() + class TClass : public TParent \ + { \ + GENERATE_SPEC(TClass, PrettyName, TFlags); \ + }; \ + void TClass::Define() //////////////////////////////////////////////////////////////// @@ -1215,14 +1214,12 @@ namespace Automatron UWorld* SelectedWorld = FindGameWorld(); -# if WITH_EDITOR +#if WITH_EDITOR // If there was no PIE world, start it and try again if (bCanUsePIEWorld && !SelectedWorld && GIsEditor) { - FDelegateHandle PIEStartedHandle = FEditorDelegates::PostPIEStarted.AddLambda( - [this, OnWorldReady, PIEStartedHandle](const bool bIsSimulating) { - FEditorDelegates::PostPIEStarted.Remove(PIEStartedHandle); - + PIEStartedHandle = + FEditorDelegates::PostPIEStarted.AddLambda([this, OnWorldReady](const bool bIsSimulating) { UWorld* SelectedWorld = FindGameWorld(); bInitializedPIE = SelectedWorld != nullptr; bInitializedWorld = bInitializedPIE; @@ -1232,7 +1229,7 @@ namespace Automatron FEditorPromotionTestUtilities::StartPIE(false); return; } -# endif +#endif if (!SelectedWorld) { @@ -1253,7 +1250,8 @@ namespace Automatron return; } -# if WITH_EDITOR +#if WITH_EDITOR + FEditorDelegates::PostPIEStarted.Remove(PIEStartedHandle); if (bInitializedPIE) { FEditorPromotionTestUtilities::EndPIE(); @@ -1261,7 +1259,7 @@ namespace Automatron bInitializedWorld = false; return; } -# endif +#endif if (!bInitializedWorld) { @@ -1352,7 +1350,7 @@ namespace Automatron inline UGameInstance* FTestSpec::CreateGameInstance(const FTestWorldSettings& Settings, UObject* Context) { UClass* GameInstanceClass = Settings.GameInstance.Get(); - if(!GameInstanceClass) + if (!GameInstanceClass) { FSoftClassPath GameInstanceClassName = GetDefault()->GameInstanceClass; GameInstanceClass = GameInstanceClassName.TryLoadClass(); @@ -1420,7 +1418,8 @@ namespace Automatron inline bool FTestSpec::SetGameMode(UWorld* World, FTestWorldSettings& Settings) { - if ((!World->IsNetMode(NM_DedicatedServer) && !World->IsNetMode(NM_ListenServer)) || World->GetAuthGameMode()) + if ((!World->IsNetMode(NM_DedicatedServer) && !World->IsNetMode(NM_ListenServer)) || + World->GetAuthGameMode()) { return false; } From 6de677dcdfda794f07de68ca3da992ce9d8d733d Mon Sep 17 00:00:00 2001 From: muit Date: Wed, 11 Oct 2023 23:59:35 +0200 Subject: [PATCH 32/39] Build fixes for Windows --- .../ClassFilter/ClassFilterHelpers.h | 1 - .../Private/LifetimeComponent.cpp | 5 +- .../SaveExtension/Private/SEFileHelpers.cpp | 34 ++-- Source/SaveExtension/Private/SaveManager.cpp | 155 ++++++++++++++---- Source/SaveExtension/Private/SaveSlot.cpp | 2 + Source/SaveExtension/Private/SaveSlotData.cpp | 5 +- .../Private/Serialization/Records.cpp | 29 ++-- .../SaveExtension/Public/LifetimeComponent.h | 7 - Source/SaveExtension/Public/SaveManager.h | 84 +--------- Source/SaveExtension/Public/SaveSlot.h | 1 + .../Public/Serialization/Records.h | 17 +- .../Public/Serialization/SEDataTask.h | 32 +--- 12 files changed, 178 insertions(+), 194 deletions(-) diff --git a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h index f1187fa..9aee42b 100644 --- a/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h +++ b/Source/Editor/Private/Customizations/ClassFilter/ClassFilterHelpers.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/Source/SaveExtension/Private/LifetimeComponent.cpp b/Source/SaveExtension/Private/LifetimeComponent.cpp index 59fda04..0652bfe 100644 --- a/Source/SaveExtension/Private/LifetimeComponent.cpp +++ b/Source/SaveExtension/Private/LifetimeComponent.cpp @@ -1,6 +1,7 @@ // Copyright 2015-2024 Piperift. All Rights Reserved. #include "LifetimeComponent.h" + #include "SaveExtension.h" @@ -10,7 +11,7 @@ void ULifetimeComponent::BeginPlay() { Super::BeginPlay(); - if (USaveManager* Manager = GetManager()) + if (USaveManager* Manager = USaveManager::Get(this)) { Manager->SubscribeForEvents(this); @@ -30,7 +31,7 @@ void ULifetimeComponent::BeginPlay() void ULifetimeComponent::EndPlay(EEndPlayReason::Type Reason) { - if (USaveManager* Manager = GetManager()) + if (USaveManager* Manager = USaveManager::Get(this)) { // If manager is loading, it has probably manually destroyed // this actor and its not a natural destroy diff --git a/Source/SaveExtension/Private/SEFileHelpers.cpp b/Source/SaveExtension/Private/SEFileHelpers.cpp index 5e4dccb..72f26b8 100644 --- a/Source/SaveExtension/Private/SEFileHelpers.cpp +++ b/Source/SaveExtension/Private/SEFileHelpers.cpp @@ -3,25 +3,26 @@ #include "SEFileHelpers.h" #include "SaveExtension.h" -#include "Serialization/SEArchive.h" +#include "SaveManager.h" #include "SaveSlot.h" #include "SaveSlotData.h" -#include "SaveManager.h" +#include "Serialization/SEArchive.h" +#include +#include #include #include #include #include #include +#include #include #include -#include -#include static const int SE_SAVEGAME_FILE_TYPE_TAG = 0x0001; // "sAvG" -UE::Tasks::FPipe BackendPipe{ TEXT("SaveExtensionPipe") }; +UE::Tasks::FPipe BackendPipe{TEXT("SaveExtensionPipe")}; /** Used to find next available slot id */ @@ -255,7 +256,7 @@ bool FSEFileHelpers::SaveFileSync(USaveSlot* Slot, FStringView OverrideSlotName, return false; } - FString SlotName = OverrideSlotName.IsEmpty()? Slot->Name.ToString() : FString{OverrideSlotName}; + FString SlotName = OverrideSlotName.IsEmpty() ? Slot->Name.ToString() : FString{OverrideSlotName}; FScopedFileWriter FileWriter(GetSlotPath(SlotName)); if (FileWriter.IsValid()) { @@ -268,7 +269,8 @@ bool FSEFileHelpers::SaveFileSync(USaveSlot* Slot, FStringView OverrideSlotName, return false; } -UE::Tasks::TTask FSEFileHelpers::SaveFile(USaveSlot* Slot, FString OverrideSlotName, const bool bUseCompression) +UE::Tasks::TTask FSEFileHelpers::SaveFile( + USaveSlot* Slot, FString OverrideSlotName, const bool bUseCompression) { return BackendPipe.Launch(TEXT("SaveFile"), [Slot, OverrideSlotName, bUseCompression]() { return SaveFileSync(Slot, OverrideSlotName, bUseCompression); @@ -276,7 +278,8 @@ UE::Tasks::TTask FSEFileHelpers::SaveFile(USaveSlot* Slot, FString Overrid } -USaveSlot* FSEFileHelpers::LoadFileSync(FStringView SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager) +USaveSlot* FSEFileHelpers::LoadFileSync( + FStringView SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager) { TRACE_CPUPROFILER_EVENT_SCOPE(FSEFileHelpers::LoadFileSync); if (SlotName.IsEmpty() && SlotHint) @@ -298,18 +301,17 @@ USaveSlot* FSEFileHelpers::LoadFileSync(FStringView SlotName, USaveSlot* SlotHin { TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeData) Slot->AssignData(Cast( - DeserializeObject(Slot->GetData(), File.DataClassName, Slot, File.DataBytes)) - ); + DeserializeObject(Slot->GetData(), File.DataClassName, Slot, File.DataBytes))); } return Slot; } return nullptr; } -UE::Tasks::TTask FSEFileHelpers::LoadFile(FString SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager) +UE::Tasks::TTask FSEFileHelpers::LoadFile( + FString SlotName, USaveSlot* SlotHint, bool bLoadData, const USaveManager* Manager) { - return BackendPipe.Launch(TEXT("LoadFile"), [SlotName, SlotHint, bLoadData, Manager]() - { + return BackendPipe.Launch(TEXT("LoadFile"), [SlotName, SlotHint, bLoadData, Manager]() { USaveSlot* Slot = LoadFileSync(SlotName, SlotHint, bLoadData, Manager); // In case we create the slot from async loading thread if (Slot) @@ -348,11 +350,11 @@ FString FSEFileHelpers::GetSlotPath(FStringView SlotName) void FSEFileHelpers::FindAllFilesSync(TArray& FoundSlots) { FSEFindSlotVisitor Visitor{FoundSlots}; - FPlatformFileManager::Get().GetPlatformFile().IterateDirectory( - *FSEFileHelpers::GetSaveFolder(), Visitor); + FPlatformFileManager::Get().GetPlatformFile().IterateDirectory(*FSEFileHelpers::GetSaveFolder(), Visitor); } -UObject* FSEFileHelpers::DeserializeObject(UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes) +UObject* FSEFileHelpers::DeserializeObject( + UObject* Hint, FStringView ClassName, const UObject* Outer, const TArray& Bytes) { UObject* Object = Hint; diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index e8ec793..a39394c 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -2,13 +2,13 @@ #include "SaveManager.h" +#include "SEFileHelpers.h" #include "SaveExtension.h" #include "SaveSettings.h" -#include "SEFileHelpers.h" -#include "Serialization/SEDataTask_LoadLevel.h" -#include "Serialization/SEDataTask_SaveLevel.h" #include "Serialization/SEDataTask_Load.h" +#include "Serialization/SEDataTask_LoadLevel.h" #include "Serialization/SEDataTask_Save.h" +#include "Serialization/SEDataTask_SaveLevel.h" #include #include @@ -28,14 +28,13 @@ // From SaveGameSystem.cpp void OnAsyncComplete(TFunction Callback) { - // NB. Using Ticker because AsyncTask may run during async package loading which may not be suitable for save data - FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda( - [Callback = MoveTemp(Callback)](float) -> bool - { + // NB. Using Ticker because AsyncTask may run during async package loading which may not be suitable for + // save data + FTSTicker::GetCoreTicker().AddTicker( + FTickerDelegate::CreateLambda([Callback = MoveTemp(Callback)](float) -> bool { Callback(); return false; - } - )); + })); } // BEGIN Async Actions @@ -128,10 +127,12 @@ class FSEPreloadSlotsAction : public FPendingLatentAction , CallbackTarget(LatentInfo.CallbackTarget) { Result = ESEContinue::InProgress; - Manager->PreloadAllSlots([this](const TArray& InSlots) { - Slots = InSlots; - Result = ESEContinue::Continue; - }, bSortByRecent); + Manager->PreloadAllSlots( + [this](const TArray& InSlots) { + Slots = InSlots; + Result = ESEContinue::Continue; + }, + bSortByRecent); } virtual void UpdateOperation(FLatentResponse& Response) override { @@ -242,13 +243,28 @@ bool USaveManager::SaveSlot(FName SlotName, bool bOverrideIfNeeded, bool bScreen // 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(); + .Setup(SlotName, bOverrideIfNeeded, bScreenshot, Size.Width, Size.Height) + .Bind(OnSaved) + .Start(); return Task.IsSucceeded() || Task.IsScheduled(); } +bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded, bool bScreenshot, + const FScreenshotSize Size, FOnGameSaved OnSaved) +{ + if (!Slot) + { + return false; + } + return SaveSlot(Slot->Name, bOverrideIfNeeded, bScreenshot, Size, OnSaved); +} + +bool USaveManager::SaveActiveSlot(bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) +{ + return SaveSlot(ActiveSlot, true, bScreenshot, Size, OnSaved); +} + bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) { if (!CanLoadOrSave() || !IsSlotSaved(SlotName)) @@ -262,18 +278,25 @@ bool USaveManager::LoadSlot(FName SlotName, FOnGameLoaded OnLoaded) return Task.IsSucceeded() || Task.IsScheduled(); } -void USaveManager::PreloadAllSlots(FSEOnAllSlotsPreloaded Callback, bool bSortByRecent) +bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded) { - FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() + if (!Slot) { + return false; + } + return LoadSlot(Slot->Name, OnLoaded); +} + +void USaveManager::PreloadAllSlots(FSEOnAllSlotsPreloaded Callback, bool bSortByRecent) +{ + FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, Callback, bSortByRecent]() { TArray Slots; PreloadAllSlotsSync(Slots, bSortByRecent); if (Callback) { - OnAsyncComplete([Slots = MoveTemp(Slots), Callback]() - { - for(auto* Slot : Slots) + OnAsyncComplete([Slots = MoveTemp(Slots), Callback]() { + for (auto* Slot : Slots) { Slot->ClearInternalFlags(EInternalObjectFlags::Async); } @@ -296,16 +319,15 @@ void USaveManager::PreloadAllSlotsSync(TArray& Slots, bool bSortByRe FScopedFileReader Reader(FSEFileHelpers::GetSlotPath(FileName)); if (Reader.IsValid()) { - LoadedFiles.AddDefaulted_GetRef() - .Read(Reader, true); + LoadedFiles.AddDefaulted_GetRef().Read(Reader, true); } } Slots.Reserve(Slots.Num() + LoadedFiles.Num()); for (const auto& File : LoadedFiles) { - auto* Slot = Cast( - FSEFileHelpers::DeserializeObject(nullptr, File.ClassName, this, File.Bytes)); + auto* Slot = + Cast(FSEFileHelpers::DeserializeObject(nullptr, File.ClassName, this, File.Bytes)); if (Slot) { Slots.Add(Slot); @@ -328,8 +350,7 @@ bool USaveManager::DeleteSlotByNameSync(FName SlotName) void USaveManager::DeleteSlotByName(FName SlotName) { - FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, SlotName]() - { + FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, SlotName]() { DeleteSlotByNameSync(SlotName); }); } @@ -349,13 +370,11 @@ int32 USaveManager::DeleteAllSlotsSync() void USaveManager::DeleteAllSlots(FSEOnAllSlotsDeleted Callback) { - FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, Callback]() - { + FSEFileHelpers::GetPipe().Launch(UE_SOURCE_LOCATION, [this, Callback]() { const int32 Count = DeleteAllSlotsSync(); if (Callback) { - OnAsyncComplete([Count, Callback]() - { + OnAsyncComplete([Count, Callback]() { Callback(Count); }); } @@ -380,7 +399,8 @@ void USaveManager::BPSaveSlotByName(FName SlotName, bool bScreenshot, const FScr Result = ESEContinueOrFail::Failed; } -void USaveManager::BPLoadSlotByName(FName SlotName, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) +void USaveManager::BPLoadSlotByName( + FName SlotName, ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo) { if (UWorld* World = GetWorld()) { @@ -419,8 +439,8 @@ void USaveManager::BPDeleteAllSlots(ESEContinue& Result, struct FLatentActionInf if (LatentActionManager.FindExistingAction( LatentInfo.CallbackTarget, LatentInfo.UUID) == nullptr) { - LatentActionManager.AddNewAction( - LatentInfo.CallbackTarget, LatentInfo.UUID, new FDeleteAllSlotsAction(this, Result, LatentInfo)); + LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, + new FDeleteAllSlotsAction(this, Result, LatentInfo)); } } } @@ -429,7 +449,7 @@ USaveSlot* USaveManager::PreloadSlot(FName SlotName) { USaveSlot* Slot = nullptr; const FString NameStr = SlotName.ToString(); - Slot = FSEFileHelpers::LoadFileSync(NameStr, nullptr, true, this); + Slot = FSEFileHelpers::LoadFileSync(NameStr, nullptr, true, this); return Slot; } @@ -510,7 +530,9 @@ void USaveManager::DeserializeStreamingLevel(ULevelStreaming* LevelStreaming) void USaveManager::FinishTask(FSEDataTask* Task) { - Tasks.RemoveAll([Task](auto& TaskPtr) { return TaskPtr.Get() == Task; }); + Tasks.RemoveAll([Task](auto& TaskPtr) { + return TaskPtr.Get() == Task; + }); // Start next task if (Tasks.Num() > 0) @@ -648,3 +670,64 @@ UWorld* USaveManager::GetWorld() const return GetGameInstance()->GetWorld(); } + +inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, const FScreenshotSize Size, + ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) +{ + if (!Slot) + { + Result = ESEContinueOrFail::Failed; + return; + } + BPSaveSlotByName(Slot->Name, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); +} + +void USaveManager::BPLoadSlot(const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) +{ + if (!Slot) + { + Result = ESEContinueOrFail::Failed; + return; + } + BPLoadSlotByName(Slot->Name, Result, MoveTemp(LatentInfo)); +} + +void USaveManager::IterateSubscribedInterfaces(TFunction&& Callback) +{ + for (const TScriptInterface& Interface : SubscribedInterfaces) + { + if (UObject* const Object = Interface.GetObject()) + { + Callback(Object); + } + } +} + +USaveManager* USaveManager::Get(const UWorld* World) +{ + if (World) + { + return UGameInstance::GetSubsystem(World->GetGameInstance()); + } + return nullptr; +} +USaveManager* USaveManager::Get(const UObject* Context) +{ + return USaveManager::Get( + GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull)); +} + +bool USaveManager::IsTickable() const +{ + return !HasAnyFlags(RF_ClassDefaultObject) && IsValid(this); +} + +UWorld* USaveManager::GetTickableGameObjectWorld() const +{ + return bTickWithGameWorld ? GetWorld() : nullptr; +} + +TStatId USaveManager::GetStatId() const +{ + RETURN_QUICK_DECLARE_CYCLE_STAT(USaveManager, STATGROUP_Tickables); +} diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 1d0cfce..228e708 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -4,12 +4,14 @@ #include "SEFileHelpers.h" +#include #include #include #include #include #include #include +#include void USaveSlot::PostInitProperties() diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index d7555ab..66398f9 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -2,6 +2,7 @@ #include "SaveSlotData.h" +#include #include @@ -31,7 +32,7 @@ void USaveSlotData::CleanRecords(bool bKeepSublevels) FPlayerRecord& USaveSlotData::FindOrAddPlayerRecord(const FUniqueNetIdRepl& UniqueId) { - return Players[Players.AddUnique({UniqueId})]; + return Players[Players.AddUnique(FPlayerRecord(UniqueId))]; } FPlayerRecord* USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId) @@ -58,7 +59,7 @@ bool USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, UPARAM(Re bool USaveSlotData::RemovePlayerRecord(const FUniqueNetIdRepl& UniqueId) { - return Players.RemoveAll([&UniqueId](const FPlayerRecord& Record){ + return Players.RemoveAll([&UniqueId](const FPlayerRecord& Record) { return Record.UniqueId == UniqueId; }) > 0; } diff --git a/Source/SaveExtension/Private/Serialization/Records.cpp b/Source/SaveExtension/Private/Serialization/Records.cpp index f2d33f7..9c141e5 100644 --- a/Source/SaveExtension/Private/Serialization/Records.cpp +++ b/Source/SaveExtension/Private/Serialization/Records.cpp @@ -2,15 +2,16 @@ #include "Serialization/Records.h" -#include "SaveExtension.h" #include "ClassFilter.h" +#include "SaveExtension.h" #include "SaveSlotData.h" #include "Serialization/SEArchive.h" -#include -#include +#include +#include #include - +#include +#include ///////////////////////////////////////////////////// @@ -84,13 +85,19 @@ bool FActorRecord::Serialize(FArchive& Ar) return true; } +bool FPlayerRecord::operator==(const FPlayerRecord& Other) const +{ + return UniqueId == Other.UniqueId; +} + const FName SERecords::TagNoTransform{"!SaveTransform"}; const FName SERecords::TagNoPhysics{"!SavePhysics"}; const FName SERecords::TagNoTags{"!SaveTags"}; -void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter) +void SERecords::SerializeActor( + const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter) { TRACE_CPUPROFILER_EVENT_SCOPE(SerializeActor); @@ -174,14 +181,14 @@ void SERecords::SerializeActor(const AActor* Actor, FActorRecord& Record, const const_cast(Actor)->Serialize(Archive); } -bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter) +bool SERecords::DeserializeActor( + AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter) { TRACE_CPUPROFILER_EVENT_SCOPE(DeserializeActor); if (Actor->GetClass() != Record.Class) { - UE_LOG( - LogSaveExtension, Log, TEXT("Actor '{}' exists but class doesn't match"), Record.Name); + UE_LOG(LogSaveExtension, Log, TEXT("Actor '{}' exists but class doesn't match"), Record.Name); return false; } @@ -252,7 +259,8 @@ bool SERecords::DeserializeActor(AActor* Actor, const FActorRecord& Record, cons return true; } -void SERecords::SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter) +void SERecords::SerializePlayer( + const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter) { check(PlayerState); @@ -271,7 +279,8 @@ void SERecords::SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& } } -void SERecords::DeserializePlayer(APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter) +void SERecords::DeserializePlayer( + APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter) { check(PlayerState); check(PlayerState->GetUniqueId() == Record.UniqueId); diff --git a/Source/SaveExtension/Public/LifetimeComponent.h b/Source/SaveExtension/Public/LifetimeComponent.h index 0475315..a01260b 100644 --- a/Source/SaveExtension/Public/LifetimeComponent.h +++ b/Source/SaveExtension/Public/LifetimeComponent.h @@ -11,7 +11,6 @@ #include "LifetimeComponent.generated.h" - DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLifetimeStartSignature); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLifetimeSavedSignature); @@ -46,12 +45,6 @@ class SAVEEXTENSION_API ULifetimeComponent : public UActorComponent, public ISav virtual void OnLoadFinished(const FSELevelFilter& Filter, bool bError); - USaveManager* GetManager() const - { - return USaveManager::Get(GetWorld()); - } - - /***********************************************************************/ /* EVENTS */ /***********************************************************************/ diff --git a/Source/SaveExtension/Public/SaveManager.h b/Source/SaveExtension/Public/SaveManager.h index 35a73a1..e9929d8 100644 --- a/Source/SaveExtension/Public/SaveManager.h +++ b/Source/SaveExtension/Public/SaveManager.h @@ -371,88 +371,6 @@ class SAVEEXTENSION_API USaveManager : public UGameInstanceSubsystem, public FTi /***********************************************************************/ public: /** Get the global save manager */ + static USaveManager* Get(const UWorld* World); static USaveManager* Get(const UObject* ContextObject); }; - - -inline bool USaveManager::SaveSlot(const USaveSlot* Slot, bool bOverrideIfNeeded, bool bScreenshot, - const FScreenshotSize Size, FOnGameSaved OnSaved) -{ - if (!Slot) - { - return false; - } - return SaveSlot(Slot->Name, bOverrideIfNeeded, bScreenshot, Size, OnSaved); -} - -inline void USaveManager::BPSaveSlot(const USaveSlot* Slot, bool bScreenshot, const FScreenshotSize Size, - ESEContinueOrFail& Result, struct FLatentActionInfo LatentInfo, bool bOverrideIfNeeded) -{ - if (!Slot) - { - Result = ESEContinueOrFail::Failed; - return; - } - BPSaveSlotByName(Slot->Name, bScreenshot, Size, Result, MoveTemp(LatentInfo), bOverrideIfNeeded); -} - -inline bool USaveManager::SaveActiveSlot(bool bScreenshot, const FScreenshotSize Size, FOnGameSaved OnSaved) -{ - return SaveSlot(ActiveSlot, true, bScreenshot, Size, OnSaved); -} - -inline bool USaveManager::LoadSlot(const USaveSlot* Slot, FOnGameLoaded OnLoaded) -{ - if (!Slot) - { - return false; - } - return LoadSlot(Slot->Name, OnLoaded); -} - -inline void USaveManager::BPLoadSlot( - const USaveSlot* Slot, ESEContinueOrFail& Result, FLatentActionInfo LatentInfo) -{ - if (!Slot) - { - Result = ESEContinueOrFail::Failed; - return; - } - BPLoadSlotByName(Slot->Name, Result, MoveTemp(LatentInfo)); -} - -inline void USaveManager::IterateSubscribedInterfaces(TFunction&& Callback) -{ - for (const TScriptInterface& Interface : SubscribedInterfaces) - { - if (UObject* const Object = Interface.GetObject()) - { - Callback(Object); - } - } -} - -inline USaveManager* USaveManager::Get(const UObject* Context) -{ - UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); - if (World) - { - return UGameInstance::GetSubsystem(World->GetGameInstance()); - } - return nullptr; -} - -inline bool USaveManager::IsTickable() const -{ - return !HasAnyFlags(RF_ClassDefaultObject) && IsValid(this); -} - -inline UWorld* USaveManager::GetTickableGameObjectWorld() const -{ - return bTickWithGameWorld ? GetWorld() : nullptr; -} - -inline TStatId USaveManager::GetStatId() const -{ - RETURN_QUICK_DECLARE_CYCLE_STAT(USaveManager, STATGROUP_Tickables); -} diff --git a/Source/SaveExtension/Public/SaveSlot.h b/Source/SaveExtension/Public/SaveSlot.h index 8d585fe..765aaf2 100644 --- a/Source/SaveExtension/Public/SaveSlot.h +++ b/Source/SaveExtension/Public/SaveSlot.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "SaveSlot.generated.h" diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index a04167d..14d920a 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -8,8 +8,10 @@ struct FSEClassFilter; +struct FUniqueNetIdRepl; class USaveSlotData; class APlayerState; +class USubsystem; USTRUCT() @@ -134,10 +136,9 @@ struct FPlayerRecord FActorRecord Pawn; - bool operator==(const FPlayerRecord& Other) const - { - return UniqueId == Other.UniqueId; - } + FPlayerRecord() = default; + FPlayerRecord(const FUniqueNetIdRepl& UniqueId) : UniqueId(UniqueId) {} + bool operator==(const FPlayerRecord& Other) const; }; @@ -150,8 +151,10 @@ namespace SERecords void SerializeActor(const AActor* Actor, FActorRecord& Record, const FSEClassFilter& ComponentFilter); bool DeserializeActor(AActor* Actor, const FActorRecord& Record, const FSEClassFilter& ComponentFilter); - void SerializePlayer(const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter); - void DeserializePlayer(APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter); + void SerializePlayer( + const APlayerState* PlayerState, FPlayerRecord& Record, const FSEClassFilter& ComponentFilter); + void DeserializePlayer( + APlayerState* PlayerState, const FPlayerRecord& Record, const FSEClassFilter& ComponentFilter); bool IsSaveTag(const FName& Tag); bool StoresTransform(const AActor* Actor); @@ -159,4 +162,4 @@ namespace SERecords bool StoresTags(const AActor* Actor); bool IsProcedural(const AActor* Actor); bool StoresTags(const UActorComponent* Component); -} +} // namespace SERecords diff --git a/Source/SaveExtension/Public/Serialization/SEDataTask.h b/Source/SaveExtension/Public/Serialization/SEDataTask.h index 3357624..ebd701e 100644 --- a/Source/SaveExtension/Public/Serialization/SEDataTask.h +++ b/Source/SaveExtension/Public/Serialization/SEDataTask.h @@ -27,21 +27,18 @@ enum class ESETaskType : uint8 struct FSEDataTask { ESETaskType Type = ESETaskType::None; + private: bool bRunning = false; bool bFinished = false; bool bSucceeded = false; protected: - TObjectPtr Manager; public: - FSEDataTask(USaveManager* Manager, ESETaskType Type) - : Type(Type) - , Manager(Manager) - {} + FSEDataTask(USaveManager* Manager, ESETaskType Type) : Type(Type), Manager(Manager) {} virtual ~FSEDataTask() = default; FSEDataTask& Start(); @@ -90,28 +87,3 @@ struct FSEDataTask public: static FString GetWorldName(const UWorld* World); }; - - -///////////////////////////////////////////////////// -// FSlotDataActorsTask -// Async task to serialize actors from a level. -class FSlotDataActorsTask : public FNonAbandonableTask -{ -protected: - const bool bIsSync; - /** USE ONLY IF SYNC */ - const UWorld* const World; - /** USE ONLY IF SYNC */ - USaveSlotData* SlotData; - - const FSELevelFilter& Filter; - - - FSlotDataActorsTask( - const bool bInIsSync, const UWorld* InWorld, USaveSlotData* InSlotData, const FSELevelFilter& Filter) - : bIsSync(bInIsSync) - , World(InWorld) - , SlotData(InSlotData) - , Filter(Filter) - {} -}; From f4c9e64ad84e72cebbb64ead0d3c963d119e29a2 Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 12 Oct 2023 00:08:36 +0200 Subject: [PATCH 33/39] Fixed includes for Windows builds --- Source/SaveExtension/Private/SaveSlot.cpp | 1 + Source/SaveExtension/Private/Serialization/Records.cpp | 5 ++++- Source/SaveExtension/Public/Serialization/Records.h | 5 ++--- Source/SaveExtension/SaveExtension.Build.cs | 7 +++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 228e708..6b93fbd 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -12,6 +12,7 @@ #include #include #include +#include void USaveSlot::PostInitProperties() diff --git a/Source/SaveExtension/Private/Serialization/Records.cpp b/Source/SaveExtension/Private/Serialization/Records.cpp index 9c141e5..eda15fc 100644 --- a/Source/SaveExtension/Private/Serialization/Records.cpp +++ b/Source/SaveExtension/Private/Serialization/Records.cpp @@ -8,7 +8,6 @@ #include "Serialization/SEArchive.h" #include -#include #include #include #include @@ -85,6 +84,10 @@ bool FActorRecord::Serialize(FArchive& Ar) return true; } + +FSubsystemRecord::FSubsystemRecord(const USubsystem* Subsystem) : Super(Subsystem) {} + + bool FPlayerRecord::operator==(const FPlayerRecord& Other) const { return UniqueId == Other.UniqueId; diff --git a/Source/SaveExtension/Public/Serialization/Records.h b/Source/SaveExtension/Public/Serialization/Records.h index 14d920a..dd76414 100644 --- a/Source/SaveExtension/Public/Serialization/Records.h +++ b/Source/SaveExtension/Public/Serialization/Records.h @@ -2,13 +2,12 @@ #pragma once -#include +#include #include "Records.generated.h" struct FSEClassFilter; -struct FUniqueNetIdRepl; class USaveSlotData; class APlayerState; class USubsystem; @@ -121,7 +120,7 @@ struct FSubsystemRecord : public FObjectRecord GENERATED_BODY() FSubsystemRecord() : Super() {} - FSubsystemRecord(const USubsystem* Subsystem) : Super(Subsystem) {} + FSubsystemRecord(const USubsystem* Subsystem); }; USTRUCT(BlueprintType) diff --git a/Source/SaveExtension/SaveExtension.Build.cs b/Source/SaveExtension/SaveExtension.Build.cs index a320b5c..00c330f 100644 --- a/Source/SaveExtension/SaveExtension.Build.cs +++ b/Source/SaveExtension/SaveExtension.Build.cs @@ -17,14 +17,13 @@ public SaveExtension(ReadOnlyTargetRules Target) : base(Target) { "Core", "Engine", - "Foliage", "AIModule", "CoreUObject", - "DeveloperSettings", - "ImageWrapper", - "NavigationSystem" + "DeveloperSettings" }); + PrivateDependencyModuleNames.AddRange(new string[] { }); + if (Target.Type == TargetType.Editor) { PrivateDependencyModuleNames.AddRange(new string[] From f61aeb37ad0d8e312c3db28161218c16b17bf81f Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 12 Oct 2023 00:11:36 +0200 Subject: [PATCH 34/39] Removed unused include --- Source/SaveExtension/Private/SaveSlot.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/SaveExtension/Private/SaveSlot.cpp b/Source/SaveExtension/Private/SaveSlot.cpp index 6b93fbd..bfba652 100644 --- a/Source/SaveExtension/Private/SaveSlot.cpp +++ b/Source/SaveExtension/Private/SaveSlot.cpp @@ -4,7 +4,6 @@ #include "SEFileHelpers.h" -#include #include #include #include From 12c9b1e0420cd1a1c0ad820d9196866d1a08c14b Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 12 Oct 2023 00:17:33 +0200 Subject: [PATCH 35/39] Added missing include --- .../Private/Serialization/SEDataTask_Save.cpp | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 38c5000..270ceae 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -2,15 +2,15 @@ #include "Serialization/SEDataTask_Save.h" +#include "SEFileHelpers.h" #include "SaveExtension.h" #include "SaveManager.h" #include "SaveSlot.h" #include "SaveSlotData.h" -#include "SEFileHelpers.h" #include "Serialization/Records.h" #include "Serialization/SEArchive.h" -#include "SEFileHelpers.h" +#include #include #include #include @@ -81,7 +81,8 @@ void FSEDataTask_Save::OnStart() bWaitingThumbnail = true; Slot->CaptureThumbnail(FSEOnThumbnailCaptured::CreateLambda([this](bool bSuccess) { bWaitingThumbnail = false; - }), Width, Height); + }), + Width, Height); } // Time stats @@ -114,7 +115,7 @@ void FSEDataTask_Save::OnStart() SerializeWorld(); - if (!bWaitingThumbnail) // Tick will check if thumbnail is not ready + if (!bWaitingThumbnail) // Tick will check if thumbnail is not ready { SaveFile(); } @@ -171,7 +172,7 @@ void FSEDataTask_Save::SerializeWorld() const TArray& Levels = World->GetStreamingLevels(); PrepareAllLevels(Levels); - { // Serialization + { // Serialization UGameInstance* GameInstance = World->GetGameInstance(); if (GameInstance && Slot->bStoreGameInstance) { @@ -183,7 +184,8 @@ void FSEDataTask_Save::SerializeWorld() SlotData->GameInstance = MoveTemp(Record); SlotData->GameInstanceSubsystems.Reset(); - for(UGameInstanceSubsystem* Subsystem : GameInstance->GetSubsystemArray()) + for (UGameInstanceSubsystem* Subsystem : + GameInstance->GetSubsystemArray()) { if (SubsystemFilter.IsAllowed(Subsystem->GetClass())) { @@ -196,7 +198,7 @@ void FSEDataTask_Save::SerializeWorld() } SlotData->WorldSubsystems.Reset(); - for(UWorldSubsystem* Subsystem : World->GetSubsystemArray()) + for (UWorldSubsystem* Subsystem : World->GetSubsystemArray()) { if (SubsystemFilter.IsAllowed(Subsystem->GetClass())) { @@ -240,8 +242,7 @@ void FSEDataTask_Save::PrepareLevel(const ULevel* Level, FLevelRecord& LevelReco LevelRecord.Filter.BakeAllowedClasses(); } -void FSEDataTask_Save::SerializeLevel( - const ULevel* Level, const ULevelStreaming* StreamingLevel) +void FSEDataTask_Save::SerializeLevel(const ULevel* Level, const ULevelStreaming* StreamingLevel) { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SerializeLevel); check(IsValid(Level)); @@ -251,10 +252,10 @@ void FSEDataTask_Save::SerializeLevel( SELog(Slot, "Level '" + LevelName.ToString() + "'", FColor::Green, false, 1); // Find level record. By default, main level - auto& LevelRecord = StreamingLevel? *FindLevelRecord(*SlotData, StreamingLevel) : SlotData->RootLevel; + auto& LevelRecord = StreamingLevel ? *FindLevelRecord(*SlotData, StreamingLevel) : SlotData->RootLevel; const FSELevelFilter& Filter = LevelRecord.Filter; - LevelRecord.CleanRecords(); // Empty level record before serializing it + LevelRecord.CleanRecords(); // Empty level record before serializing it TArray ActorsToSerialize; for (AActor* Actor : Level->Actors) @@ -266,16 +267,19 @@ void FSEDataTask_Save::SerializeLevel( } LevelRecord.Actors.SetNum(ActorsToSerialize.Num()); - ParallelFor(ActorsToSerialize.Num(), [&LevelRecord, &ActorsToSerialize, &Filter](int32 i) - { - SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter.ComponentFilter); - }, Slot->ShouldSerializeAsync()? EParallelForFlags::None : EParallelForFlags::ForceSingleThread); + ParallelFor( + ActorsToSerialize.Num(), + [&LevelRecord, &ActorsToSerialize, &Filter](int32 i) { + SERecords::SerializeActor(ActorsToSerialize[i], LevelRecord.Actors[i], Filter.ComponentFilter); + }, + Slot->ShouldSerializeAsync() ? EParallelForFlags::None : EParallelForFlags::ForceSingleThread); } void FSEDataTask_Save::SaveFile() { TRACE_CPUPROFILER_EVENT_SCOPE(FSEDataTask_Save::SaveFile); - SaveFileTask = FSEFileHelpers::SaveFile(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); + SaveFileTask = + FSEFileHelpers::SaveFile(Manager->GetActiveSlot(), SlotName.ToString(), Slot->bUseCompression); if (!Slot->ShouldSaveFileAsync()) { From 5945f801cc1825b06edee9a32344e0f5e0318283 Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 12 Oct 2023 10:37:54 +0200 Subject: [PATCH 36/39] [CICD] Enable cache --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dadb965..c5958ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,12 @@ jobs: - name: Download Piperift Scripts run: git clone https://github.com/PipeRift/CICDScripts Scripts + - name: Cache Build + uses: actions/cache@v3 + with: + path: Build + key: ${{ matrix.os }}-${{ matrix.config }}-${{ matrix.version }} + - name: Build Plugin run: python Scripts/build.py plugin -n ${{ env.CI_PLUGIN }} -p . From 015f1c22df868ca87f1e9ff469072a859f2a9ad6 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 17 Oct 2023 16:10:07 +0200 Subject: [PATCH 37/39] Small fix --- Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp index 270ceae..bd0b104 100644 --- a/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp +++ b/Source/SaveExtension/Private/Serialization/SEDataTask_Save.cpp @@ -21,6 +21,7 @@ FSEDataTask_Save::FSEDataTask_Save(USaveManager* Manager, USaveSlot* Slot) : FSEDataTask(Manager, ESETaskType::Save) + , Slot(Slot) , SlotData(Slot->AssureData()) {} From 208c3258333427fd837374d2a74154ee9d5f74a7 Mon Sep 17 00:00:00 2001 From: muit Date: Tue, 31 Oct 2023 11:31:17 +0100 Subject: [PATCH 38/39] Re-enabled events --- Source/SaveExtension/Private/SaveManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index a39394c..ef9c47a 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -628,7 +628,8 @@ void USaveManager::OnLoadFinished(const bool bError) { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadFinished); - /*IterateSubscribedInterfaces([&Filter, bError](auto* Object) { + FSELevelFilter Filter; + IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -637,7 +638,7 @@ void USaveManager::OnLoadFinished(const bool bError) Interface->OnLoadFinished(Filter, bError); } ISaveExtensionInterface::Execute_ReceiveOnLoadFinished(Object, Filter, bError); - });*/ + }); if (!bError) { From af71e19fd57a0eb88ecae1a1401dc101e3949cf0 Mon Sep 17 00:00:00 2001 From: muit Date: Thu, 7 Dec 2023 21:59:03 +0100 Subject: [PATCH 39/39] Fixed incorrect UPARAM and re-enabled events --- Source/SaveExtension/Private/SaveManager.cpp | 17 ++++++++++------- Source/SaveExtension/Private/SaveSlotData.cpp | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Source/SaveExtension/Private/SaveManager.cpp b/Source/SaveExtension/Private/SaveManager.cpp index ef9c47a..97b73e0 100644 --- a/Source/SaveExtension/Private/SaveManager.cpp +++ b/Source/SaveExtension/Private/SaveManager.cpp @@ -574,7 +574,8 @@ void USaveManager::OnSaveBegan() TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnSaveBegan); // TODO: Needs reworking - /*IterateSubscribedInterfaces([&Filter](auto* Object) { + FSELevelFilter Filter; + IterateSubscribedInterfaces([&Filter](auto* Object) { check(Object->template Implements()); // C++ event @@ -583,7 +584,7 @@ void USaveManager::OnSaveBegan() Interface->OnSaveBegan(Filter); } ISaveExtensionInterface::Execute_ReceiveOnSaveBegan(Object, Filter); - });*/ + }); } void USaveManager::OnSaveFinished(const bool bError) @@ -591,7 +592,8 @@ void USaveManager::OnSaveFinished(const bool bError) TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnSaveFinished); // TODO: Needs reworking - /*IterateSubscribedInterfaces([&Filter, bError](auto* Object) { + FSELevelFilter Filter; + IterateSubscribedInterfaces([&Filter, bError](auto* Object) { check(Object->template Implements()); // C++ event @@ -600,7 +602,7 @@ void USaveManager::OnSaveFinished(const bool bError) Interface->OnSaveFinished(Filter, bError); } ISaveExtensionInterface::Execute_ReceiveOnSaveFinished(Object, Filter, bError); - });*/ + }); if (!bError) { @@ -611,8 +613,9 @@ void USaveManager::OnSaveFinished(const bool bError) void USaveManager::OnLoadBegan() { TRACE_CPUPROFILER_EVENT_SCOPE(USaveManager::OnLoadBegan); - - /*IterateSubscribedInterfaces([&Filter](auto* Object) { + + FSELevelFilter Filter; + IterateSubscribedInterfaces([&Filter](auto* Object) { check(Object->template Implements()); // C++ event @@ -621,7 +624,7 @@ void USaveManager::OnLoadBegan() Interface->OnLoadBegan(Filter); } ISaveExtensionInterface::Execute_ReceiveOnLoadBegan(Object, Filter); - });*/ + }); } void USaveManager::OnLoadFinished(const bool bError) diff --git a/Source/SaveExtension/Private/SaveSlotData.cpp b/Source/SaveExtension/Private/SaveSlotData.cpp index 66398f9..8cf4397 100644 --- a/Source/SaveExtension/Private/SaveSlotData.cpp +++ b/Source/SaveExtension/Private/SaveSlotData.cpp @@ -47,7 +47,7 @@ FPlayerRecord* USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId) return nullptr; } -bool USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, UPARAM(Ref) FPlayerRecord& Record) +bool USaveSlotData::FindPlayerRecord(const FUniqueNetIdRepl& UniqueId, FPlayerRecord& Record) { if (FPlayerRecord* FoundRecord = FindPlayerRecord(UniqueId)) {